mirror of https://github.com/YosysHQ/yosys.git
Merge pull request #3185 from YosysHQ/micko/co_sim
Add co-simulation in sim pass
This commit is contained in:
commit
d7f7227ce8
8
Makefile
8
Makefile
|
@ -594,9 +594,11 @@ $(eval $(call add_include_file,kernel/satgen.h))
|
|||
$(eval $(call add_include_file,kernel/qcsat.h))
|
||||
$(eval $(call add_include_file,kernel/ff.h))
|
||||
$(eval $(call add_include_file,kernel/ffinit.h))
|
||||
$(eval $(call add_include_file,kernel/fstdata.h))
|
||||
$(eval $(call add_include_file,kernel/mem.h))
|
||||
$(eval $(call add_include_file,libs/ezsat/ezsat.h))
|
||||
$(eval $(call add_include_file,libs/ezsat/ezminisat.h))
|
||||
$(eval $(call add_include_file,libs/fst/fstapi.h))
|
||||
$(eval $(call add_include_file,libs/sha1/sha1.h))
|
||||
$(eval $(call add_include_file,libs/json11/json11.hpp))
|
||||
$(eval $(call add_include_file,passes/fsm/fsmdata.h))
|
||||
|
@ -618,7 +620,7 @@ ifneq ($(ABCEXTERNAL),)
|
|||
kernel/yosys.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'
|
||||
endif
|
||||
endif
|
||||
OBJS += kernel/cellaigs.o kernel/celledges.o kernel/satgen.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o
|
||||
OBJS += kernel/cellaigs.o kernel/celledges.o kernel/satgen.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/fstdata.o
|
||||
|
||||
kernel/log.o: CXXFLAGS += -DYOSYS_SRC='"$(YOSYS_SRC)"'
|
||||
kernel/yosys.o: CXXFLAGS += -DYOSYS_DATDIR='"$(DATDIR)"' -DYOSYS_PROGRAM_PREFIX='"$(PROGRAM_PREFIX)"'
|
||||
|
@ -642,6 +644,10 @@ OBJS += libs/minisat/SimpSolver.o
|
|||
OBJS += libs/minisat/Solver.o
|
||||
OBJS += libs/minisat/System.o
|
||||
|
||||
OBJS += libs/fst/fstapi.o
|
||||
OBJS += libs/fst/fastlz.o
|
||||
OBJS += libs/fst/lz4.o
|
||||
|
||||
include $(YOSYS_SRC)/frontends/*/Makefile.inc
|
||||
include $(YOSYS_SRC)/passes/*/Makefile.inc
|
||||
include $(YOSYS_SRC)/backends/*/Makefile.inc
|
||||
|
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2022 Miodrag Milanovic <micko@yosyshq.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/fstdata.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
||||
|
||||
FstData::FstData(std::string filename) : ctx(nullptr)
|
||||
{
|
||||
const std::vector<std::string> g_units = { "s", "ms", "us", "ns", "ps", "fs", "as", "zs" };
|
||||
ctx = (fstReaderContext *)fstReaderOpen(filename.c_str());
|
||||
if (!ctx)
|
||||
log_error("Error opening '%s'\n", filename.c_str());
|
||||
int scale = (int)fstReaderGetTimescale(ctx);
|
||||
timescale = pow(10.0, scale);
|
||||
timescale_str = "";
|
||||
int unit = 0;
|
||||
int zeros = 0;
|
||||
if (scale > 0) {
|
||||
zeros = scale;
|
||||
} else {
|
||||
if ((scale % 3) == 0) {
|
||||
zeros = (-scale % 3);
|
||||
unit = (-scale / 3);
|
||||
} else {
|
||||
zeros = 3 - (-scale % 3);
|
||||
unit = (-scale / 3) + 1;
|
||||
}
|
||||
}
|
||||
for (int i=0;i<zeros; i++) timescale_str += "0";
|
||||
timescale_str += g_units[unit];
|
||||
extractVarNames();
|
||||
}
|
||||
|
||||
FstData::~FstData()
|
||||
{
|
||||
if (ctx)
|
||||
fstReaderClose(ctx);
|
||||
}
|
||||
|
||||
uint64_t FstData::getStartTime() { return fstReaderGetStartTime(ctx); }
|
||||
|
||||
uint64_t FstData::getEndTime() { return fstReaderGetEndTime(ctx); }
|
||||
|
||||
fstHandle FstData::getHandle(std::string name) {
|
||||
if (name_to_handle.find(name) != name_to_handle.end())
|
||||
return name_to_handle[name];
|
||||
else
|
||||
return 0;
|
||||
};
|
||||
|
||||
static std::string remove_spaces(std::string str)
|
||||
{
|
||||
str.erase(std::remove(str.begin(), str.end(), ' '), str.end());
|
||||
return str;
|
||||
}
|
||||
|
||||
void FstData::extractVarNames()
|
||||
{
|
||||
struct fstHier *h;
|
||||
intptr_t snum = 0;
|
||||
|
||||
while ((h = fstReaderIterateHier(ctx))) {
|
||||
switch (h->htyp) {
|
||||
case FST_HT_SCOPE: {
|
||||
snum++;
|
||||
std::string fst_scope_name = fstReaderPushScope(ctx, h->u.scope.name, (void *)(snum));
|
||||
scopes.push_back(fst_scope_name);
|
||||
break;
|
||||
}
|
||||
case FST_HT_UPSCOPE: {
|
||||
fstReaderPopScope(ctx);
|
||||
snum = fstReaderGetCurrentScopeLen(ctx) ? (intptr_t)fstReaderGetCurrentScopeUserInfo(ctx) : 0;
|
||||
break;
|
||||
}
|
||||
case FST_HT_VAR: {
|
||||
FstVar var;
|
||||
var.id = h->u.var.handle;
|
||||
var.is_alias = h->u.var.is_alias;
|
||||
var.name = remove_spaces(h->u.var.name);
|
||||
var.scope = scopes.back();
|
||||
var.width = h->u.var.length;
|
||||
vars.push_back(var);
|
||||
if (!var.is_alias)
|
||||
handle_to_var[h->u.var.handle] = var;
|
||||
std::string clean_name;
|
||||
for(size_t i=0;i<strlen(h->u.var.name);i++)
|
||||
{
|
||||
char c = h->u.var.name[i];
|
||||
if(c==' ') break;
|
||||
clean_name += c;
|
||||
}
|
||||
if (clean_name[0]=='\\')
|
||||
clean_name = clean_name.substr(1);
|
||||
//log("adding %s.%s\n",var.scope.c_str(), clean_name.c_str());
|
||||
|
||||
name_to_handle[var.scope+"."+clean_name] = h->u.var.handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void reconstruct_edges_varlen(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen)
|
||||
{
|
||||
FstData *ptr = (FstData*)user_data;
|
||||
ptr->reconstruct_edges_callback(pnt_time, pnt_facidx, pnt_value, plen);
|
||||
}
|
||||
|
||||
static void reconstruct_edges(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value)
|
||||
{
|
||||
FstData *ptr = (FstData*)user_data;
|
||||
uint32_t plen = (pnt_value) ? strlen((const char *)pnt_value) : 0;
|
||||
ptr->reconstruct_edges_callback(pnt_time, pnt_facidx, pnt_value, plen);
|
||||
}
|
||||
|
||||
void FstData::reconstruct_edges_callback(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t /* plen */)
|
||||
{
|
||||
std::string val = std::string((const char *)pnt_value);
|
||||
std::string prev = last_data[pnt_facidx];
|
||||
if (pnt_time>=start_time) {
|
||||
if (prev!="1" && val=="1")
|
||||
edges.push_back(pnt_time);
|
||||
if (prev!="0" && val=="0")
|
||||
edges.push_back(pnt_time);
|
||||
}
|
||||
last_data[pnt_facidx] = val;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> FstData::getAllEdges(std::vector<fstHandle> &signal, uint64_t start, uint64_t end)
|
||||
{
|
||||
start_time = start;
|
||||
end_time = end;
|
||||
last_data.clear();
|
||||
for(auto &s : signal) {
|
||||
last_data[s] = "x";
|
||||
}
|
||||
edges.clear();
|
||||
fstReaderSetLimitTimeRange(ctx, start_time, end_time);
|
||||
fstReaderClrFacProcessMaskAll(ctx);
|
||||
for(const auto sig : signal)
|
||||
fstReaderSetFacProcessMask(ctx,sig);
|
||||
fstReaderIterBlocks2(ctx, reconstruct_edges, reconstruct_edges_varlen, this, nullptr);
|
||||
return edges;
|
||||
}
|
||||
|
||||
static void reconstruct_clb_varlen_attimes(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen)
|
||||
{
|
||||
FstData *ptr = (FstData*)user_data;
|
||||
ptr->reconstruct_callback_attimes(pnt_time, pnt_facidx, pnt_value, plen);
|
||||
}
|
||||
|
||||
static void reconstruct_clb_attimes(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value)
|
||||
{
|
||||
FstData *ptr = (FstData*)user_data;
|
||||
uint32_t plen = (pnt_value) ? strlen((const char *)pnt_value) : 0;
|
||||
ptr->reconstruct_callback_attimes(pnt_time, pnt_facidx, pnt_value, plen);
|
||||
}
|
||||
|
||||
void FstData::reconstruct_callback_attimes(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t /* plen */)
|
||||
{
|
||||
if (sample_times_ndx >= sample_times.size()) return;
|
||||
|
||||
uint64_t time = sample_times[sample_times_ndx];
|
||||
// if we are past the timestamp
|
||||
if (pnt_time > time) {
|
||||
for (auto const& c : last_data)
|
||||
{
|
||||
handle_to_data[c.first].push_back(std::make_pair(time,c.second));
|
||||
size_t index = handle_to_data[c.first].size() - 1;
|
||||
time_to_index[c.first][time] = index;
|
||||
}
|
||||
sample_times_ndx++;
|
||||
}
|
||||
// always update last_data
|
||||
last_data[pnt_facidx] = std::string((const char *)pnt_value);
|
||||
}
|
||||
|
||||
void FstData::reconstructAtTimes(std::vector<fstHandle> &signal, std::vector<uint64_t> time)
|
||||
{
|
||||
handle_to_data.clear();
|
||||
time_to_index.clear();
|
||||
last_data.clear();
|
||||
sample_times_ndx = 0;
|
||||
sample_times = time;
|
||||
fstReaderSetUnlimitedTimeRange(ctx);
|
||||
fstReaderClrFacProcessMaskAll(ctx);
|
||||
for(const auto sig : signal)
|
||||
fstReaderSetFacProcessMask(ctx,sig);
|
||||
fstReaderIterBlocks2(ctx, reconstruct_clb_attimes, reconstruct_clb_varlen_attimes, this, nullptr);
|
||||
|
||||
if (time_to_index[signal.back()].count(time.back())==0) {
|
||||
for (auto const& c : last_data)
|
||||
{
|
||||
handle_to_data[c.first].push_back(std::make_pair(time.back(),c.second));
|
||||
size_t index = handle_to_data[c.first].size() - 1;
|
||||
time_to_index[c.first][time.back()] = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FstData::reconstructAllAtTimes(std::vector<uint64_t> time)
|
||||
{
|
||||
handle_to_data.clear();
|
||||
time_to_index.clear();
|
||||
last_data.clear();
|
||||
sample_times_ndx = 0;
|
||||
sample_times = time;
|
||||
|
||||
fstReaderSetUnlimitedTimeRange(ctx);
|
||||
fstReaderSetFacProcessMaskAll(ctx);
|
||||
fstReaderIterBlocks2(ctx, reconstruct_clb_attimes, reconstruct_clb_varlen_attimes, this, nullptr);
|
||||
|
||||
if (time_to_index[1].count(time.back())==0) {
|
||||
for (auto const& c : last_data)
|
||||
{
|
||||
handle_to_data[c.first].push_back(std::make_pair(time.back(),c.second));
|
||||
size_t index = handle_to_data[c.first].size() - 1;
|
||||
time_to_index[c.first][time.back()] = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string FstData::valueAt(fstHandle signal, uint64_t time)
|
||||
{
|
||||
if (handle_to_data.find(signal) == handle_to_data.end())
|
||||
log_error("Signal id %d not found\n", (int)signal);
|
||||
auto &data = handle_to_data[signal];
|
||||
if (time_to_index[signal].count(time)!=0) {
|
||||
size_t index = time_to_index[signal][time];
|
||||
return data.at(index).second;
|
||||
} else {
|
||||
log_error("No data for signal %d at time %d\n", (int)signal, (int)time);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2022 Miodrag Milanovic <micko@yosyshq.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FSTDATA_H
|
||||
#define FSTDATA_H
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "libs/fst/fstapi.h"
|
||||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
struct FstVar
|
||||
{
|
||||
fstHandle id;
|
||||
std::string name;
|
||||
bool is_alias;
|
||||
std::string scope;
|
||||
int width;
|
||||
};
|
||||
|
||||
class FstData
|
||||
{
|
||||
public:
|
||||
FstData(std::string filename);
|
||||
~FstData();
|
||||
|
||||
uint64_t getStartTime();
|
||||
uint64_t getEndTime();
|
||||
|
||||
std::vector<FstVar>& getVars() { return vars; };
|
||||
|
||||
void reconstruct_edges_callback(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen);
|
||||
std::vector<uint64_t> getAllEdges(std::vector<fstHandle> &signal, uint64_t start_time, uint64_t end_time);
|
||||
|
||||
void reconstruct_callback_attimes(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen);
|
||||
void reconstructAtTimes(std::vector<fstHandle> &signal,std::vector<uint64_t> time);
|
||||
void reconstructAllAtTimes(std::vector<uint64_t> time);
|
||||
|
||||
std::string valueAt(fstHandle signal, uint64_t time);
|
||||
fstHandle getHandle(std::string name);
|
||||
double getTimescale() { return timescale; }
|
||||
const char *getTimescaleString() { return timescale_str.c_str(); }
|
||||
private:
|
||||
void extractVarNames();
|
||||
|
||||
struct fstReaderContext *ctx;
|
||||
std::vector<std::string> scopes;
|
||||
std::vector<FstVar> vars;
|
||||
std::map<fstHandle, FstVar> handle_to_var;
|
||||
std::map<std::string, fstHandle> name_to_handle;
|
||||
std::map<fstHandle, std::vector<std::pair<uint64_t, std::string>>> handle_to_data;
|
||||
std::map<fstHandle, std::string> last_data;
|
||||
std::map<fstHandle, std::map<uint64_t, size_t>> time_to_index;
|
||||
std::vector<uint64_t> sample_times;
|
||||
size_t sample_times_ndx;
|
||||
double timescale;
|
||||
std::string timescale_str;
|
||||
uint64_t start_time;
|
||||
uint64_t end_time;
|
||||
std::vector<uint64_t> edges;
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
#endif
|
|
@ -0,0 +1,130 @@
|
|||
See fstapi.h for the values for the FST_BL_XXX enums.
|
||||
|
||||
===========================================================================
|
||||
|
||||
compressed wrapper (typically over whole file)
|
||||
|
||||
uint8_t FST_BL_ZWRAPPER
|
||||
uint64_t section length
|
||||
uint64_t length of uncompressed data
|
||||
[zlib compressed data]
|
||||
|
||||
===========================================================================
|
||||
|
||||
header block
|
||||
|
||||
uint8_t FST_BL_HDR
|
||||
uint64_t section length
|
||||
uint64_t start time
|
||||
uint64_t end time
|
||||
double endian test for "e"
|
||||
uint64_t memory used by writer
|
||||
uint64_t scope creation count
|
||||
uint64_t var creation count
|
||||
uint64_t max var idcode
|
||||
uint64_t vc section count
|
||||
int8_t timescale exponent
|
||||
[128 bytes] version
|
||||
[128 bytes] date
|
||||
|
||||
===========================================================================
|
||||
|
||||
geometry block
|
||||
|
||||
uint8_t FST_BL_GEOM
|
||||
uint64_t section length
|
||||
uint64_t length of uncompressed geometry data
|
||||
uint64_t maxhandle
|
||||
[compressed data]
|
||||
|
||||
(length of compressed data is section length - 24)
|
||||
|
||||
===========================================================================
|
||||
|
||||
hierarchy block
|
||||
|
||||
uint8_t FST_BL_HIER
|
||||
uint64_t section length
|
||||
uint64_t length of uncompressed hier data
|
||||
[zlib compressed data]
|
||||
|
||||
or
|
||||
|
||||
uint8_t FST_BL_HIER_LZ4
|
||||
uint64_t section length
|
||||
uint64_t length of uncompressed hier data
|
||||
[lz4 compressed data]
|
||||
|
||||
uint8_t FST_BL_HIER_LZ4DUO
|
||||
uint64_t section length
|
||||
uint64_t length of uncompressed hier data
|
||||
varint length of hier data compressed once with lz4
|
||||
[lz4 double compressed data]
|
||||
|
||||
|
||||
===========================================================================
|
||||
|
||||
dumpon/off block
|
||||
|
||||
uint8_t FST_BL_BLACKOUT
|
||||
uint64_t section length
|
||||
varint num blackouts (section below is repeated this # times)
|
||||
[
|
||||
uint8_t on/off (nonzero = on)
|
||||
varint delta time
|
||||
]
|
||||
|
||||
===========================================================================
|
||||
|
||||
1..n value change blocks:
|
||||
|
||||
// header
|
||||
|
||||
uint8_t FST_BL_VCDATA (or FST_BL_VCDATA_DYN_ALIAS)
|
||||
uint64_t section length
|
||||
uint64_t begin time of section
|
||||
uint64_t end time of section
|
||||
uint64_t amount of buffer memory required in reader for full vc traversal
|
||||
varint maxvalpos (length of uncompressed data)
|
||||
varint length of compressed data
|
||||
varint maxhandle associated with this checkpoint data
|
||||
[compressed data]
|
||||
|
||||
---
|
||||
|
||||
// value changes
|
||||
|
||||
varint maxhandle associated with the value change data
|
||||
uint8_t pack type ('F' is fastlz, '4' is lz4,
|
||||
others ['Z'/'!'] are zlib)
|
||||
|
||||
varint chain 0 compressed data length (0 = uncompressed)
|
||||
[compressed data]
|
||||
...
|
||||
varint chain n compressed data length (0 = uncompressed)
|
||||
[compressed data]
|
||||
|
||||
---
|
||||
|
||||
// index: chain pointer table (from 0..maxhandle-1)
|
||||
|
||||
varint if &1 == 1, this is <<1 literal delta
|
||||
if &1 == 0, this is <<1 RLE count of zeros
|
||||
if == 0, next varint is handle of prev chain to use,
|
||||
bit only if FST_BL_VCDATA_DYN_ALIAS or
|
||||
later VCDATA format
|
||||
|
||||
---
|
||||
|
||||
uint64_t index length (subtract from here to get index position)
|
||||
|
||||
---
|
||||
|
||||
[compressed data for time section]
|
||||
uint64_t uncompressed data length in bytes
|
||||
uint64_t compressed data length in bytes
|
||||
uint64_t number of time items
|
||||
|
||||
// end of section
|
||||
|
||||
===========================================================================
|
|
@ -0,0 +1,27 @@
|
|||
/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
|
||||
*/
|
||||
#define HAVE_ALLOCA_H 1
|
||||
|
||||
/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
|
||||
#define HAVE_FSEEKO 1
|
||||
|
||||
/* Define to 1 if you have the `pthread' library (-lpthread). */
|
||||
#define HAVE_LIBPTHREAD 1
|
||||
|
||||
/* Define to 1 if you have the `realpath' function. */
|
||||
#define HAVE_REALPATH 1
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#undef HAVE_ALLOCA_H
|
||||
#undef HAVE_REALPATH
|
||||
#endif
|
||||
#if defined(_MSC_VER)
|
||||
#undef HAVE_ALLOCA_H
|
||||
#undef HAVE_REALPATH
|
||||
#undef HAVE_LIBPTHREAD
|
||||
#undef HAVE_FSEEKO
|
||||
#endif
|
||||
|
||||
# ifndef __STDC_FORMAT_MACROS
|
||||
# define __STDC_FORMAT_MACROS 1
|
||||
# endif
|
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
FastLZ - lightning-fast lossless compression library
|
||||
|
||||
Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
|
||||
Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
|
||||
Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "fastlz.h"
|
||||
|
||||
#if !defined(FASTLZ__COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR)
|
||||
|
||||
/*
|
||||
* Always check for bound when decompressing.
|
||||
* Generally it is best to leave it defined.
|
||||
*/
|
||||
#define FASTLZ_SAFE
|
||||
|
||||
/*
|
||||
* Give hints to the compiler for branch prediction optimization.
|
||||
*/
|
||||
#if defined(__GNUC__) && (__GNUC__ > 2)
|
||||
#define FASTLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
|
||||
#define FASTLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
|
||||
#else
|
||||
#define FASTLZ_EXPECT_CONDITIONAL(c) (c)
|
||||
#define FASTLZ_UNEXPECT_CONDITIONAL(c) (c)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Use inlined functions for supported systems.
|
||||
*/
|
||||
#if defined(__GNUC__) || defined(__DMC__) || defined(__POCC__) || defined(__WATCOMC__) || defined(__SUNPRO_C)
|
||||
#define FASTLZ_INLINE inline
|
||||
#elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(__LCC__)
|
||||
#define FASTLZ_INLINE __inline
|
||||
#else
|
||||
#define FASTLZ_INLINE
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Prevent accessing more than 8-bit at once, except on x86 architectures.
|
||||
*/
|
||||
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||
#define FASTLZ_STRICT_ALIGN
|
||||
#if defined(__i386__) || defined(__386) /* GNU C, Sun Studio */
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#elif defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(__amd64) /* GNU C */
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#elif defined(_M_IX86) /* Intel, MSVC */
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#elif defined(__386)
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#elif defined(_X86_) /* MinGW */
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#elif defined(__I86__) /* Digital Mars */
|
||||
#undef FASTLZ_STRICT_ALIGN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* prototypes */
|
||||
int fastlz_compress(const void *input, int length, void *output);
|
||||
int fastlz_compress_level(int level, const void *input, int length, void *output);
|
||||
int fastlz_decompress(const void *input, int length, void *output, int maxout);
|
||||
|
||||
#define MAX_COPY 32
|
||||
#define MAX_LEN 264 /* 256 + 8 */
|
||||
#define MAX_DISTANCE 8192
|
||||
|
||||
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||
#define FASTLZ_READU16(p) *((const flzuint16 *)(p))
|
||||
#else
|
||||
#define FASTLZ_READU16(p) ((p)[0] | (p)[1] << 8)
|
||||
#endif
|
||||
|
||||
#define HASH_LOG 13
|
||||
#define HASH_SIZE (1 << HASH_LOG)
|
||||
#define HASH_MASK (HASH_SIZE - 1)
|
||||
#define HASH_FUNCTION(v, p) \
|
||||
{ \
|
||||
v = FASTLZ_READU16(p); \
|
||||
v ^= FASTLZ_READU16(p + 1) ^ (v >> (16 - HASH_LOG)); \
|
||||
v &= HASH_MASK; \
|
||||
}
|
||||
|
||||
#undef FASTLZ_LEVEL
|
||||
#define FASTLZ_LEVEL 1
|
||||
|
||||
#undef FASTLZ_COMPRESSOR
|
||||
#undef FASTLZ_DECOMPRESSOR
|
||||
#define FASTLZ_COMPRESSOR fastlz1_compress
|
||||
#define FASTLZ_DECOMPRESSOR fastlz1_decompress
|
||||
static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void *input, int length, void *output);
|
||||
static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void *input, int length, void *output, int maxout);
|
||||
#include "fastlz.cc"
|
||||
|
||||
#undef FASTLZ_LEVEL
|
||||
#define FASTLZ_LEVEL 2
|
||||
|
||||
#undef MAX_DISTANCE
|
||||
#define MAX_DISTANCE 8191
|
||||
#define MAX_FARDISTANCE (65535 + MAX_DISTANCE - 1)
|
||||
|
||||
#undef FASTLZ_COMPRESSOR
|
||||
#undef FASTLZ_DECOMPRESSOR
|
||||
#define FASTLZ_COMPRESSOR fastlz2_compress
|
||||
#define FASTLZ_DECOMPRESSOR fastlz2_decompress
|
||||
static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void *input, int length, void *output);
|
||||
static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void *input, int length, void *output, int maxout);
|
||||
#include "fastlz.cc"
|
||||
|
||||
int fastlz_compress(const void *input, int length, void *output)
|
||||
{
|
||||
/* for short block, choose fastlz1 */
|
||||
if (length < 65536)
|
||||
return fastlz1_compress(input, length, output);
|
||||
|
||||
/* else... */
|
||||
return fastlz2_compress(input, length, output);
|
||||
}
|
||||
|
||||
int fastlz_decompress(const void *input, int length, void *output, int maxout)
|
||||
{
|
||||
/* magic identifier for compression level */
|
||||
int level = ((*(const flzuint8 *)input) >> 5) + 1;
|
||||
|
||||
if (level == 1)
|
||||
return fastlz1_decompress(input, length, output, maxout);
|
||||
if (level == 2)
|
||||
return fastlz2_decompress(input, length, output, maxout);
|
||||
|
||||
/* unknown level, trigger error */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fastlz_compress_level(int level, const void *input, int length, void *output)
|
||||
{
|
||||
if (level == 1)
|
||||
return fastlz1_compress(input, length, output);
|
||||
if (level == 2)
|
||||
return fastlz2_compress(input, length, output);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */
|
||||
|
||||
static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void *input, int length, void *output)
|
||||
{
|
||||
const flzuint8 *ip = (const flzuint8 *)input;
|
||||
const flzuint8 *ip_bound = ip + length - 2;
|
||||
const flzuint8 *ip_limit = ip + length - 12;
|
||||
flzuint8 *op = (flzuint8 *)output;
|
||||
|
||||
const flzuint8 *htab[HASH_SIZE];
|
||||
const flzuint8 **hslot;
|
||||
flzuint32 hval;
|
||||
|
||||
flzuint32 copy;
|
||||
|
||||
/* sanity check */
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(length < 4)) {
|
||||
if (length) {
|
||||
/* create literal copy only */
|
||||
*op++ = length - 1;
|
||||
ip_bound++;
|
||||
while (ip <= ip_bound)
|
||||
*op++ = *ip++;
|
||||
return length + 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* initializes hash table */
|
||||
for (hslot = htab; hslot < htab + HASH_SIZE; hslot++)
|
||||
*hslot = ip;
|
||||
|
||||
/* we start with literal copy */
|
||||
copy = 2;
|
||||
*op++ = MAX_COPY - 1;
|
||||
*op++ = *ip++;
|
||||
*op++ = *ip++;
|
||||
|
||||
/* main loop */
|
||||
while (FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) {
|
||||
const flzuint8 *ref;
|
||||
flzuint32 distance;
|
||||
|
||||
/* minimum match length */
|
||||
flzuint32 len = 3;
|
||||
|
||||
/* comparison starting-point */
|
||||
const flzuint8 *anchor = ip;
|
||||
|
||||
/* check for a run */
|
||||
#if FASTLZ_LEVEL == 2
|
||||
if (ip[0] == ip[-1] && FASTLZ_READU16(ip - 1) == FASTLZ_READU16(ip + 1)) {
|
||||
distance = 1;
|
||||
/* ip += 3; */ /* scan-build, never used */
|
||||
ref = anchor - 1 + 3;
|
||||
goto match;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* find potential match */
|
||||
HASH_FUNCTION(hval, ip);
|
||||
hslot = htab + hval;
|
||||
ref = htab[hval];
|
||||
|
||||
/* calculate distance to the match */
|
||||
distance = anchor - ref;
|
||||
|
||||
/* update hash table */
|
||||
*hslot = anchor;
|
||||
|
||||
/* is this a match? check the first 3 bytes */
|
||||
if (distance == 0 ||
|
||||
#if FASTLZ_LEVEL == 1
|
||||
(distance >= MAX_DISTANCE) ||
|
||||
#else
|
||||
(distance >= MAX_FARDISTANCE) ||
|
||||
#endif
|
||||
*ref++ != *ip++ || *ref++ != *ip++ || *ref++ != *ip++)
|
||||
goto literal;
|
||||
|
||||
#if FASTLZ_LEVEL == 2
|
||||
/* far, needs at least 5-byte match */
|
||||
if (distance >= MAX_DISTANCE) {
|
||||
if (*ip++ != *ref++ || *ip++ != *ref++)
|
||||
goto literal;
|
||||
len += 2;
|
||||
}
|
||||
|
||||
match:
|
||||
#endif
|
||||
|
||||
/* last matched byte */
|
||||
ip = anchor + len;
|
||||
|
||||
/* distance is biased */
|
||||
distance--;
|
||||
|
||||
if (!distance) {
|
||||
/* zero distance means a run */
|
||||
flzuint8 x = ip[-1];
|
||||
while (ip < ip_bound)
|
||||
if (*ref++ != x)
|
||||
break;
|
||||
else
|
||||
ip++;
|
||||
} else
|
||||
for (;;) {
|
||||
/* safe because the outer check against ip limit */
|
||||
if (*ref++ != *ip++)
|
||||
break;
|
||||
if (*ref++ != *ip++)
|
||||
break;
|
||||
if (*ref++ != *ip++)
|
||||
break;
|
||||
if (*ref++ != *ip++)
|
||||
break;
|
||||
if (*ref++ != *ip++)
|
||||
break;
|
||||
if (*ref++ != *ip++)
|
||||
break;
|
||||
if (*ref++ != *ip++)
|
||||
break;
|
||||
if (*ref++ != *ip++)
|
||||
break;
|
||||
while (ip < ip_bound)
|
||||
if (*ref++ != *ip++)
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
/* if we have copied something, adjust the copy count */
|
||||
if (copy)
|
||||
/* copy is biased, '0' means 1 byte copy */
|
||||
*(op - copy - 1) = copy - 1;
|
||||
else
|
||||
/* back, to overwrite the copy count */
|
||||
op--;
|
||||
|
||||
/* reset literal counter */
|
||||
copy = 0;
|
||||
|
||||
/* length is biased, '1' means a match of 3 bytes */
|
||||
ip -= 3;
|
||||
len = ip - anchor;
|
||||
|
||||
/* encode the match */
|
||||
#if FASTLZ_LEVEL == 2
|
||||
if (distance < MAX_DISTANCE) {
|
||||
if (len < 7) {
|
||||
*op++ = (len << 5) + (distance >> 8);
|
||||
*op++ = (distance & 255);
|
||||
} else {
|
||||
*op++ = (7 << 5) + (distance >> 8);
|
||||
for (len -= 7; len >= 255; len -= 255)
|
||||
*op++ = 255;
|
||||
*op++ = len;
|
||||
*op++ = (distance & 255);
|
||||
}
|
||||
} else {
|
||||
/* far away, but not yet in the another galaxy... */
|
||||
if (len < 7) {
|
||||
distance -= MAX_DISTANCE;
|
||||
*op++ = (len << 5) + 31;
|
||||
*op++ = 255;
|
||||
*op++ = distance >> 8;
|
||||
*op++ = distance & 255;
|
||||
} else {
|
||||
distance -= MAX_DISTANCE;
|
||||
*op++ = (7 << 5) + 31;
|
||||
for (len -= 7; len >= 255; len -= 255)
|
||||
*op++ = 255;
|
||||
*op++ = len;
|
||||
*op++ = 255;
|
||||
*op++ = distance >> 8;
|
||||
*op++ = distance & 255;
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(len > MAX_LEN - 2))
|
||||
while (len > MAX_LEN - 2) {
|
||||
*op++ = (7 << 5) + (distance >> 8);
|
||||
*op++ = MAX_LEN - 2 - 7 - 2;
|
||||
*op++ = (distance & 255);
|
||||
len -= MAX_LEN - 2;
|
||||
}
|
||||
|
||||
if (len < 7) {
|
||||
*op++ = (len << 5) + (distance >> 8);
|
||||
*op++ = (distance & 255);
|
||||
} else {
|
||||
*op++ = (7 << 5) + (distance >> 8);
|
||||
*op++ = len - 7;
|
||||
*op++ = (distance & 255);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* update the hash at match boundary */
|
||||
HASH_FUNCTION(hval, ip);
|
||||
htab[hval] = ip++;
|
||||
HASH_FUNCTION(hval, ip);
|
||||
htab[hval] = ip++;
|
||||
|
||||
/* assuming literal copy */
|
||||
*op++ = MAX_COPY - 1;
|
||||
|
||||
continue;
|
||||
|
||||
literal:
|
||||
*op++ = *anchor++;
|
||||
ip = anchor;
|
||||
copy++;
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY)) {
|
||||
copy = 0;
|
||||
*op++ = MAX_COPY - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* left-over as literal copy */
|
||||
ip_bound++;
|
||||
while (ip <= ip_bound) {
|
||||
*op++ = *ip++;
|
||||
copy++;
|
||||
if (copy == MAX_COPY) {
|
||||
copy = 0;
|
||||
*op++ = MAX_COPY - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we have copied something, adjust the copy length */
|
||||
if (copy)
|
||||
*(op - copy - 1) = copy - 1;
|
||||
else
|
||||
op--;
|
||||
|
||||
#if FASTLZ_LEVEL == 2
|
||||
/* marker for fastlz2 */
|
||||
*(flzuint8 *)output |= (1 << 5);
|
||||
#endif
|
||||
|
||||
return op - (flzuint8 *)output;
|
||||
}
|
||||
|
||||
static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void *input, int length, void *output, int maxout)
|
||||
{
|
||||
const flzuint8 *ip = (const flzuint8 *)input;
|
||||
const flzuint8 *ip_limit = ip + length;
|
||||
flzuint8 *op = (flzuint8 *)output;
|
||||
flzuint8 *op_limit = op + maxout;
|
||||
flzuint32 ctrl = (*ip++) & 31;
|
||||
int loop = 1;
|
||||
|
||||
do {
|
||||
const flzuint8 *ref = op;
|
||||
flzuint32 len = ctrl >> 5;
|
||||
flzuint32 ofs = (ctrl & 31) << 8;
|
||||
|
||||
if (ctrl >= 32) {
|
||||
#if FASTLZ_LEVEL == 2
|
||||
flzuint8 code;
|
||||
#endif
|
||||
len--;
|
||||
ref -= ofs;
|
||||
if (len == 7 - 1)
|
||||
#if FASTLZ_LEVEL == 1
|
||||
len += *ip++;
|
||||
ref -= *ip++;
|
||||
#else
|
||||
do {
|
||||
code = *ip++;
|
||||
len += code;
|
||||
} while (code == 255);
|
||||
code = *ip++;
|
||||
ref -= code;
|
||||
|
||||
/* match from 16-bit distance */
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(code == 255))
|
||||
if (FASTLZ_EXPECT_CONDITIONAL(ofs == (31 << 8))) {
|
||||
ofs = (*ip++) << 8;
|
||||
ofs += *ip++;
|
||||
ref = op - ofs - MAX_DISTANCE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FASTLZ_SAFE
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(op + len + 3 > op_limit))
|
||||
return 0;
|
||||
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(ref - 1 < (flzuint8 *)output))
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
if (FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit))
|
||||
ctrl = *ip++;
|
||||
else
|
||||
loop = 0;
|
||||
|
||||
if (ref == op) {
|
||||
/* optimize copy for a run */
|
||||
flzuint8 b = ref[-1];
|
||||
*op++ = b;
|
||||
*op++ = b;
|
||||
*op++ = b;
|
||||
for (; len; --len)
|
||||
*op++ = b;
|
||||
} else {
|
||||
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||
const flzuint16 *p;
|
||||
flzuint16 *q;
|
||||
#endif
|
||||
/* copy from reference */
|
||||
ref--;
|
||||
*op++ = *ref++;
|
||||
*op++ = *ref++;
|
||||
*op++ = *ref++;
|
||||
|
||||
#if !defined(FASTLZ_STRICT_ALIGN)
|
||||
/* copy a byte, so that now it's word aligned */
|
||||
if (len & 1) {
|
||||
*op++ = *ref++;
|
||||
len--;
|
||||
}
|
||||
|
||||
/* copy 16-bit at once */
|
||||
q = (flzuint16 *)op;
|
||||
op += len;
|
||||
p = (const flzuint16 *)ref;
|
||||
for (len >>= 1; len > 4; len -= 4) {
|
||||
*q++ = *p++;
|
||||
*q++ = *p++;
|
||||
*q++ = *p++;
|
||||
*q++ = *p++;
|
||||
}
|
||||
for (; len; --len)
|
||||
*q++ = *p++;
|
||||
#else
|
||||
for (; len; --len)
|
||||
*op++ = *ref++;
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
ctrl++;
|
||||
#ifdef FASTLZ_SAFE
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit))
|
||||
return 0;
|
||||
if (FASTLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit))
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
*op++ = *ip++;
|
||||
for (--ctrl; ctrl; ctrl--)
|
||||
*op++ = *ip++;
|
||||
|
||||
loop = FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit);
|
||||
if (loop)
|
||||
ctrl = *ip++;
|
||||
}
|
||||
} while (FASTLZ_EXPECT_CONDITIONAL(loop));
|
||||
|
||||
return op - (flzuint8 *)output;
|
||||
}
|
||||
|
||||
#endif /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
FastLZ - lightning-fast lossless compression library
|
||||
|
||||
Copyright (C) 2007 Ariya Hidayat (ariya@kde.org)
|
||||
Copyright (C) 2006 Ariya Hidayat (ariya@kde.org)
|
||||
Copyright (C) 2005 Ariya Hidayat (ariya@kde.org)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef FASTLZ_H
|
||||
#define FASTLZ_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define flzuint8 uint8_t
|
||||
#define flzuint16 uint16_t
|
||||
#define flzuint32 uint32_t
|
||||
|
||||
|
||||
#define FASTLZ_VERSION 0x000100
|
||||
|
||||
#define FASTLZ_VERSION_MAJOR 0
|
||||
#define FASTLZ_VERSION_MINOR 0
|
||||
#define FASTLZ_VERSION_REVISION 0
|
||||
|
||||
#define FASTLZ_VERSION_STRING "0.1.0"
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Compress a block of data in the input buffer and returns the size of
|
||||
compressed block. The size of input buffer is specified by length. The
|
||||
minimum input buffer size is 16.
|
||||
|
||||
The output buffer must be at least 5% larger than the input buffer
|
||||
and can not be smaller than 66 bytes.
|
||||
|
||||
If the input is not compressible, the return value might be larger than
|
||||
length (input buffer size).
|
||||
|
||||
The input buffer and the output buffer can not overlap.
|
||||
*/
|
||||
|
||||
int fastlz_compress(const void* input, int length, void* output);
|
||||
|
||||
/**
|
||||
Decompress a block of compressed data and returns the size of the
|
||||
decompressed block. If error occurs, e.g. the compressed data is
|
||||
corrupted or the output buffer is not large enough, then 0 (zero)
|
||||
will be returned instead.
|
||||
|
||||
The input buffer and the output buffer can not overlap.
|
||||
|
||||
Decompression is memory safe and guaranteed not to write the output buffer
|
||||
more than what is specified in maxout.
|
||||
*/
|
||||
|
||||
int fastlz_decompress(const void* input, int length, void* output, int maxout);
|
||||
|
||||
/**
|
||||
Compress a block of data in the input buffer and returns the size of
|
||||
compressed block. The size of input buffer is specified by length. The
|
||||
minimum input buffer size is 16.
|
||||
|
||||
The output buffer must be at least 5% larger than the input buffer
|
||||
and can not be smaller than 66 bytes.
|
||||
|
||||
If the input is not compressible, the return value might be larger than
|
||||
length (input buffer size).
|
||||
|
||||
The input buffer and the output buffer can not overlap.
|
||||
|
||||
Compression level can be specified in parameter level. At the moment,
|
||||
only level 1 and level 2 are supported.
|
||||
Level 1 is the fastest compression and generally useful for short data.
|
||||
Level 2 is slightly slower but it gives better compression ratio.
|
||||
|
||||
Note that the compressed data, regardless of the level, can always be
|
||||
decompressed using the function fastlz_decompress above.
|
||||
*/
|
||||
|
||||
int fastlz_compress_level(int level, const void* input, int length, void* output);
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FASTLZ_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,500 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2018 Tony Bybell.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef FST_API_H
|
||||
#define FST_API_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#if defined(_MSC_VER)
|
||||
#include "libs/zlib/zlib.h"
|
||||
#include <io.h>
|
||||
|
||||
#include <process.h>
|
||||
|
||||
#define ftruncate _chsize_s
|
||||
#define unlink _unlink
|
||||
#define fileno _fileno
|
||||
#define lseek _lseeki64
|
||||
|
||||
#ifdef _WIN64
|
||||
#define ssize_t __int64
|
||||
#define SSIZE_MAX 9223372036854775807i64
|
||||
#else
|
||||
#define ssize_t long
|
||||
#define SSIZE_MAX 2147483647L
|
||||
#endif
|
||||
|
||||
#include "stdint.h"
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <time.h>
|
||||
|
||||
#define FST_RDLOAD "FSTLOAD | "
|
||||
|
||||
typedef uint32_t fstHandle;
|
||||
typedef uint32_t fstEnumHandle;
|
||||
|
||||
enum fstWriterPackType
|
||||
{
|
||||
FST_WR_PT_ZLIB = 0,
|
||||
FST_WR_PT_FASTLZ = 1,
|
||||
FST_WR_PT_LZ4 = 2
|
||||
};
|
||||
|
||||
enum fstFileType
|
||||
{
|
||||
FST_FT_MIN = 0,
|
||||
|
||||
FST_FT_VERILOG = 0,
|
||||
FST_FT_VHDL = 1,
|
||||
FST_FT_VERILOG_VHDL = 2,
|
||||
|
||||
FST_FT_MAX = 2
|
||||
};
|
||||
|
||||
enum fstBlockType
|
||||
{
|
||||
FST_BL_HDR = 0,
|
||||
FST_BL_VCDATA = 1,
|
||||
FST_BL_BLACKOUT = 2,
|
||||
FST_BL_GEOM = 3,
|
||||
FST_BL_HIER = 4,
|
||||
FST_BL_VCDATA_DYN_ALIAS = 5,
|
||||
FST_BL_HIER_LZ4 = 6,
|
||||
FST_BL_HIER_LZ4DUO = 7,
|
||||
FST_BL_VCDATA_DYN_ALIAS2 = 8,
|
||||
|
||||
FST_BL_ZWRAPPER = 254, /* indicates that whole trace is gz wrapped */
|
||||
FST_BL_SKIP = 255 /* used while block is being written */
|
||||
};
|
||||
|
||||
enum fstScopeType
|
||||
{
|
||||
FST_ST_MIN = 0,
|
||||
|
||||
FST_ST_VCD_MODULE = 0,
|
||||
FST_ST_VCD_TASK = 1,
|
||||
FST_ST_VCD_FUNCTION = 2,
|
||||
FST_ST_VCD_BEGIN = 3,
|
||||
FST_ST_VCD_FORK = 4,
|
||||
FST_ST_VCD_GENERATE = 5,
|
||||
FST_ST_VCD_STRUCT = 6,
|
||||
FST_ST_VCD_UNION = 7,
|
||||
FST_ST_VCD_CLASS = 8,
|
||||
FST_ST_VCD_INTERFACE = 9,
|
||||
FST_ST_VCD_PACKAGE = 10,
|
||||
FST_ST_VCD_PROGRAM = 11,
|
||||
|
||||
FST_ST_VHDL_ARCHITECTURE = 12,
|
||||
FST_ST_VHDL_PROCEDURE = 13,
|
||||
FST_ST_VHDL_FUNCTION = 14,
|
||||
FST_ST_VHDL_RECORD = 15,
|
||||
FST_ST_VHDL_PROCESS = 16,
|
||||
FST_ST_VHDL_BLOCK = 17,
|
||||
FST_ST_VHDL_FOR_GENERATE = 18,
|
||||
FST_ST_VHDL_IF_GENERATE = 19,
|
||||
FST_ST_VHDL_GENERATE = 20,
|
||||
FST_ST_VHDL_PACKAGE = 21,
|
||||
|
||||
FST_ST_MAX = 21,
|
||||
|
||||
FST_ST_GEN_ATTRBEGIN = 252,
|
||||
FST_ST_GEN_ATTREND = 253,
|
||||
|
||||
FST_ST_VCD_SCOPE = 254,
|
||||
FST_ST_VCD_UPSCOPE = 255
|
||||
};
|
||||
|
||||
enum fstVarType
|
||||
{
|
||||
FST_VT_MIN = 0, /* start of vartypes */
|
||||
|
||||
FST_VT_VCD_EVENT = 0,
|
||||
FST_VT_VCD_INTEGER = 1,
|
||||
FST_VT_VCD_PARAMETER = 2,
|
||||
FST_VT_VCD_REAL = 3,
|
||||
FST_VT_VCD_REAL_PARAMETER = 4,
|
||||
FST_VT_VCD_REG = 5,
|
||||
FST_VT_VCD_SUPPLY0 = 6,
|
||||
FST_VT_VCD_SUPPLY1 = 7,
|
||||
FST_VT_VCD_TIME = 8,
|
||||
FST_VT_VCD_TRI = 9,
|
||||
FST_VT_VCD_TRIAND = 10,
|
||||
FST_VT_VCD_TRIOR = 11,
|
||||
FST_VT_VCD_TRIREG = 12,
|
||||
FST_VT_VCD_TRI0 = 13,
|
||||
FST_VT_VCD_TRI1 = 14,
|
||||
FST_VT_VCD_WAND = 15,
|
||||
FST_VT_VCD_WIRE = 16,
|
||||
FST_VT_VCD_WOR = 17,
|
||||
FST_VT_VCD_PORT = 18,
|
||||
FST_VT_VCD_SPARRAY = 19, /* used to define the rownum (index) port for a sparse array */
|
||||
FST_VT_VCD_REALTIME = 20,
|
||||
|
||||
FST_VT_GEN_STRING =
|
||||
21, /* generic string type (max len is defined dynamically via fstWriterEmitVariableLengthValueChange) */
|
||||
|
||||
FST_VT_SV_BIT = 22,
|
||||
FST_VT_SV_LOGIC = 23,
|
||||
FST_VT_SV_INT = 24, /* declare as size = 32 */
|
||||
FST_VT_SV_SHORTINT = 25, /* declare as size = 16 */
|
||||
FST_VT_SV_LONGINT = 26, /* declare as size = 64 */
|
||||
FST_VT_SV_BYTE = 27, /* declare as size = 8 */
|
||||
FST_VT_SV_ENUM = 28, /* declare as appropriate type range */
|
||||
FST_VT_SV_SHORTREAL =
|
||||
29, /* declare and emit same as FST_VT_VCD_REAL (needs to be emitted as double, not a float) */
|
||||
|
||||
FST_VT_MAX = 29 /* end of vartypes */
|
||||
};
|
||||
|
||||
enum fstVarDir
|
||||
{
|
||||
FST_VD_MIN = 0,
|
||||
|
||||
FST_VD_IMPLICIT = 0,
|
||||
FST_VD_INPUT = 1,
|
||||
FST_VD_OUTPUT = 2,
|
||||
FST_VD_INOUT = 3,
|
||||
FST_VD_BUFFER = 4,
|
||||
FST_VD_LINKAGE = 5,
|
||||
|
||||
FST_VD_MAX = 5
|
||||
};
|
||||
|
||||
enum fstHierType
|
||||
{
|
||||
FST_HT_MIN = 0,
|
||||
|
||||
FST_HT_SCOPE = 0,
|
||||
FST_HT_UPSCOPE = 1,
|
||||
FST_HT_VAR = 2,
|
||||
FST_HT_ATTRBEGIN = 3,
|
||||
FST_HT_ATTREND = 4,
|
||||
|
||||
/* FST_HT_TREEBEGIN and FST_HT_TREEEND are not yet used by FST but are currently used when fstHier bridges other
|
||||
formats */
|
||||
FST_HT_TREEBEGIN = 5,
|
||||
FST_HT_TREEEND = 6,
|
||||
|
||||
FST_HT_MAX = 6
|
||||
};
|
||||
|
||||
enum fstAttrType
|
||||
{
|
||||
FST_AT_MIN = 0,
|
||||
|
||||
FST_AT_MISC = 0, /* self-contained: does not need matching FST_HT_ATTREND */
|
||||
FST_AT_ARRAY = 1,
|
||||
FST_AT_ENUM = 2,
|
||||
FST_AT_PACK = 3,
|
||||
|
||||
FST_AT_MAX = 3
|
||||
};
|
||||
|
||||
enum fstMiscType
|
||||
{
|
||||
FST_MT_MIN = 0,
|
||||
|
||||
FST_MT_COMMENT = 0, /* use fstWriterSetComment() to emit */
|
||||
FST_MT_ENVVAR = 1, /* use fstWriterSetEnvVar() to emit */
|
||||
FST_MT_SUPVAR = 2, /* use fstWriterCreateVar2() to emit */
|
||||
FST_MT_PATHNAME = 3, /* reserved for fstWriterSetSourceStem() string -> number management */
|
||||
FST_MT_SOURCESTEM = 4, /* use fstWriterSetSourceStem() to emit */
|
||||
FST_MT_SOURCEISTEM = 5, /* use fstWriterSetSourceInstantiationStem() to emit */
|
||||
FST_MT_VALUELIST = 6, /* use fstWriterSetValueList() to emit, followed by fstWriterCreateVar*() */
|
||||
FST_MT_ENUMTABLE = 7, /* use fstWriterCreateEnumTable() and fstWriterEmitEnumTableRef() to emit */
|
||||
FST_MT_UNKNOWN = 8,
|
||||
|
||||
FST_MT_MAX = 8
|
||||
};
|
||||
|
||||
enum fstArrayType
|
||||
{
|
||||
FST_AR_MIN = 0,
|
||||
|
||||
FST_AR_NONE = 0,
|
||||
FST_AR_UNPACKED = 1,
|
||||
FST_AR_PACKED = 2,
|
||||
FST_AR_SPARSE = 3,
|
||||
|
||||
FST_AR_MAX = 3
|
||||
};
|
||||
|
||||
enum fstEnumValueType
|
||||
{
|
||||
FST_EV_SV_INTEGER = 0,
|
||||
FST_EV_SV_BIT = 1,
|
||||
FST_EV_SV_LOGIC = 2,
|
||||
FST_EV_SV_INT = 3,
|
||||
FST_EV_SV_SHORTINT = 4,
|
||||
FST_EV_SV_LONGINT = 5,
|
||||
FST_EV_SV_BYTE = 6,
|
||||
FST_EV_SV_UNSIGNED_INTEGER = 7,
|
||||
FST_EV_SV_UNSIGNED_BIT = 8,
|
||||
FST_EV_SV_UNSIGNED_LOGIC = 9,
|
||||
FST_EV_SV_UNSIGNED_INT = 10,
|
||||
FST_EV_SV_UNSIGNED_SHORTINT = 11,
|
||||
FST_EV_SV_UNSIGNED_LONGINT = 12,
|
||||
FST_EV_SV_UNSIGNED_BYTE = 13,
|
||||
|
||||
FST_EV_REG = 14,
|
||||
FST_EV_TIME = 15,
|
||||
|
||||
FST_EV_MAX = 15
|
||||
};
|
||||
|
||||
enum fstPackType
|
||||
{
|
||||
FST_PT_NONE = 0,
|
||||
FST_PT_UNPACKED = 1,
|
||||
FST_PT_PACKED = 2,
|
||||
FST_PT_TAGGED_PACKED = 3,
|
||||
|
||||
FST_PT_MAX = 3
|
||||
};
|
||||
|
||||
enum fstSupplementalVarType
|
||||
{
|
||||
FST_SVT_MIN = 0,
|
||||
|
||||
FST_SVT_NONE = 0,
|
||||
|
||||
FST_SVT_VHDL_SIGNAL = 1,
|
||||
FST_SVT_VHDL_VARIABLE = 2,
|
||||
FST_SVT_VHDL_CONSTANT = 3,
|
||||
FST_SVT_VHDL_FILE = 4,
|
||||
FST_SVT_VHDL_MEMORY = 5,
|
||||
|
||||
FST_SVT_MAX = 5
|
||||
};
|
||||
|
||||
enum fstSupplementalDataType
|
||||
{
|
||||
FST_SDT_MIN = 0,
|
||||
|
||||
FST_SDT_NONE = 0,
|
||||
|
||||
FST_SDT_VHDL_BOOLEAN = 1,
|
||||
FST_SDT_VHDL_BIT = 2,
|
||||
FST_SDT_VHDL_BIT_VECTOR = 3,
|
||||
FST_SDT_VHDL_STD_ULOGIC = 4,
|
||||
FST_SDT_VHDL_STD_ULOGIC_VECTOR = 5,
|
||||
FST_SDT_VHDL_STD_LOGIC = 6,
|
||||
FST_SDT_VHDL_STD_LOGIC_VECTOR = 7,
|
||||
FST_SDT_VHDL_UNSIGNED = 8,
|
||||
FST_SDT_VHDL_SIGNED = 9,
|
||||
FST_SDT_VHDL_INTEGER = 10,
|
||||
FST_SDT_VHDL_REAL = 11,
|
||||
FST_SDT_VHDL_NATURAL = 12,
|
||||
FST_SDT_VHDL_POSITIVE = 13,
|
||||
FST_SDT_VHDL_TIME = 14,
|
||||
FST_SDT_VHDL_CHARACTER = 15,
|
||||
FST_SDT_VHDL_STRING = 16,
|
||||
|
||||
FST_SDT_MAX = 16,
|
||||
|
||||
FST_SDT_SVT_SHIFT_COUNT =
|
||||
10, /* FST_SVT_* is ORed in by fstWriterCreateVar2() to the left after shifting FST_SDT_SVT_SHIFT_COUNT */
|
||||
FST_SDT_ABS_MAX = ((1 << (FST_SDT_SVT_SHIFT_COUNT)) - 1)
|
||||
};
|
||||
|
||||
struct fstHier
|
||||
{
|
||||
unsigned char htyp;
|
||||
|
||||
union
|
||||
{
|
||||
/* if htyp == FST_HT_SCOPE */
|
||||
struct fstHierScope
|
||||
{
|
||||
unsigned char typ; /* FST_ST_MIN ... FST_ST_MAX */
|
||||
const char *name;
|
||||
const char *component;
|
||||
uint32_t name_length; /* strlen(u.scope.name) */
|
||||
uint32_t component_length; /* strlen(u.scope.component) */
|
||||
} scope;
|
||||
|
||||
/* if htyp == FST_HT_VAR */
|
||||
struct fstHierVar
|
||||
{
|
||||
unsigned char typ; /* FST_VT_MIN ... FST_VT_MAX */
|
||||
unsigned char direction; /* FST_VD_MIN ... FST_VD_MAX */
|
||||
unsigned char svt_workspace; /* zeroed out by FST reader, for client code use */
|
||||
unsigned char sdt_workspace; /* zeroed out by FST reader, for client code use */
|
||||
unsigned int sxt_workspace; /* zeroed out by FST reader, for client code use */
|
||||
const char *name;
|
||||
uint32_t length;
|
||||
fstHandle handle;
|
||||
uint32_t name_length; /* strlen(u.var.name) */
|
||||
unsigned is_alias : 1;
|
||||
} var;
|
||||
|
||||
/* if htyp == FST_HT_ATTRBEGIN */
|
||||
struct fstHierAttr
|
||||
{
|
||||
unsigned char typ; /* FST_AT_MIN ... FST_AT_MAX */
|
||||
unsigned char subtype; /* from fstMiscType, fstArrayType, fstEnumValueType, fstPackType */
|
||||
const char *name;
|
||||
uint64_t arg; /* number of array elements, struct members, or some other payload (possibly ignored) */
|
||||
uint64_t arg_from_name; /* for when name is overloaded as a variable-length integer (FST_AT_MISC +
|
||||
FST_MT_SOURCESTEM) */
|
||||
uint32_t name_length; /* strlen(u.attr.name) */
|
||||
} attr;
|
||||
} u;
|
||||
};
|
||||
|
||||
struct fstETab
|
||||
{
|
||||
char *name;
|
||||
uint32_t elem_count;
|
||||
char **literal_arr;
|
||||
char **val_arr;
|
||||
};
|
||||
|
||||
/*
|
||||
* writer functions
|
||||
*/
|
||||
void fstWriterClose(void *ctx);
|
||||
void *fstWriterCreate(const char *nam, int use_compressed_hier);
|
||||
fstEnumHandle fstWriterCreateEnumTable(void *ctx, const char *name, uint32_t elem_count, unsigned int min_valbits,
|
||||
const char **literal_arr, const char **val_arr);
|
||||
/* used for Verilog/SV */
|
||||
fstHandle fstWriterCreateVar(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam,
|
||||
fstHandle aliasHandle);
|
||||
/* future expansion for VHDL and other languages. The variable type, data type, etc map onto
|
||||
the current Verilog/SV one. The "type" string is optional for a more verbose or custom description */
|
||||
fstHandle fstWriterCreateVar2(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam,
|
||||
fstHandle aliasHandle, const char *type, enum fstSupplementalVarType svt,
|
||||
enum fstSupplementalDataType sdt);
|
||||
void fstWriterEmitDumpActive(void *ctx, int enable);
|
||||
void fstWriterEmitEnumTableRef(void *ctx, fstEnumHandle handle);
|
||||
void fstWriterEmitValueChange(void *ctx, fstHandle handle, const void *val);
|
||||
void fstWriterEmitValueChange32(void *ctx, fstHandle handle, uint32_t bits, uint32_t val);
|
||||
void fstWriterEmitValueChange64(void *ctx, fstHandle handle, uint32_t bits, uint64_t val);
|
||||
void fstWriterEmitValueChangeVec32(void *ctx, fstHandle handle, uint32_t bits, const uint32_t *val);
|
||||
void fstWriterEmitValueChangeVec64(void *ctx, fstHandle handle, uint32_t bits, const uint64_t *val);
|
||||
void fstWriterEmitVariableLengthValueChange(void *ctx, fstHandle handle, const void *val, uint32_t len);
|
||||
void fstWriterEmitTimeChange(void *ctx, uint64_t tim);
|
||||
void fstWriterFlushContext(void *ctx);
|
||||
int fstWriterGetDumpSizeLimitReached(void *ctx);
|
||||
int fstWriterGetFseekFailed(void *ctx);
|
||||
void fstWriterSetAttrBegin(void *ctx, enum fstAttrType attrtype, int subtype, const char *attrname, uint64_t arg);
|
||||
void fstWriterSetAttrEnd(void *ctx);
|
||||
void fstWriterSetComment(void *ctx, const char *comm);
|
||||
void fstWriterSetDate(void *ctx, const char *dat);
|
||||
void fstWriterSetDumpSizeLimit(void *ctx, uint64_t numbytes);
|
||||
void fstWriterSetEnvVar(void *ctx, const char *envvar);
|
||||
void fstWriterSetFileType(void *ctx, enum fstFileType filetype);
|
||||
void fstWriterSetPackType(void *ctx, enum fstWriterPackType typ);
|
||||
void fstWriterSetParallelMode(void *ctx, int enable);
|
||||
void fstWriterSetRepackOnClose(void *ctx, int enable); /* type = 0 (none), 1 (libz) */
|
||||
void fstWriterSetScope(void *ctx, enum fstScopeType scopetype, const char *scopename, const char *scopecomp);
|
||||
void fstWriterSetSourceInstantiationStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath);
|
||||
void fstWriterSetSourceStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath);
|
||||
void fstWriterSetTimescale(void *ctx, int ts);
|
||||
void fstWriterSetTimescaleFromString(void *ctx, const char *s);
|
||||
void fstWriterSetTimezero(void *ctx, int64_t tim);
|
||||
void fstWriterSetUpscope(void *ctx);
|
||||
void fstWriterSetValueList(void *ctx, const char *vl);
|
||||
void fstWriterSetVersion(void *ctx, const char *vers);
|
||||
|
||||
/*
|
||||
* reader functions
|
||||
*/
|
||||
void fstReaderClose(void *ctx);
|
||||
void fstReaderClrFacProcessMask(void *ctx, fstHandle facidx);
|
||||
void fstReaderClrFacProcessMaskAll(void *ctx);
|
||||
uint64_t fstReaderGetAliasCount(void *ctx);
|
||||
const char *fstReaderGetCurrentFlatScope(void *ctx);
|
||||
void *fstReaderGetCurrentScopeUserInfo(void *ctx);
|
||||
int fstReaderGetCurrentScopeLen(void *ctx);
|
||||
const char *fstReaderGetDateString(void *ctx);
|
||||
int fstReaderGetDoubleEndianMatchState(void *ctx);
|
||||
uint64_t fstReaderGetDumpActivityChangeTime(void *ctx, uint32_t idx);
|
||||
unsigned char fstReaderGetDumpActivityChangeValue(void *ctx, uint32_t idx);
|
||||
uint64_t fstReaderGetEndTime(void *ctx);
|
||||
int fstReaderGetFacProcessMask(void *ctx, fstHandle facidx);
|
||||
int fstReaderGetFileType(void *ctx);
|
||||
int fstReaderGetFseekFailed(void *ctx);
|
||||
fstHandle fstReaderGetMaxHandle(void *ctx);
|
||||
uint64_t fstReaderGetMemoryUsedByWriter(void *ctx);
|
||||
uint32_t fstReaderGetNumberDumpActivityChanges(void *ctx);
|
||||
uint64_t fstReaderGetScopeCount(void *ctx);
|
||||
uint64_t fstReaderGetStartTime(void *ctx);
|
||||
signed char fstReaderGetTimescale(void *ctx);
|
||||
int64_t fstReaderGetTimezero(void *ctx);
|
||||
uint64_t fstReaderGetValueChangeSectionCount(void *ctx);
|
||||
char *fstReaderGetValueFromHandleAtTime(void *ctx, uint64_t tim, fstHandle facidx, char *buf);
|
||||
uint64_t fstReaderGetVarCount(void *ctx);
|
||||
const char *fstReaderGetVersionString(void *ctx);
|
||||
struct fstHier *fstReaderIterateHier(void *ctx);
|
||||
int fstReaderIterateHierRewind(void *ctx);
|
||||
int fstReaderIterBlocks(void *ctx,
|
||||
void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time, fstHandle facidx,
|
||||
const unsigned char *value),
|
||||
void *user_callback_data_pointer, FILE *vcdhandle);
|
||||
int fstReaderIterBlocks2(void *ctx,
|
||||
void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time,
|
||||
fstHandle facidx, const unsigned char *value),
|
||||
void (*value_change_callback_varlen)(void *user_callback_data_pointer, uint64_t time,
|
||||
fstHandle facidx, const unsigned char *value,
|
||||
uint32_t len),
|
||||
void *user_callback_data_pointer, FILE *vcdhandle);
|
||||
void fstReaderIterBlocksSetNativeDoublesOnCallback(void *ctx, int enable);
|
||||
void *fstReaderOpen(const char *nam);
|
||||
void *fstReaderOpenForUtilitiesOnly(void);
|
||||
const char *fstReaderPopScope(void *ctx);
|
||||
int fstReaderProcessHier(void *ctx, FILE *vcdhandle);
|
||||
const char *fstReaderPushScope(void *ctx, const char *nam, void *user_info);
|
||||
void fstReaderResetScope(void *ctx);
|
||||
void fstReaderSetFacProcessMask(void *ctx, fstHandle facidx);
|
||||
void fstReaderSetFacProcessMaskAll(void *ctx);
|
||||
void fstReaderSetLimitTimeRange(void *ctx, uint64_t start_time, uint64_t end_time);
|
||||
void fstReaderSetUnlimitedTimeRange(void *ctx);
|
||||
void fstReaderSetVcdExtensions(void *ctx, int enable);
|
||||
|
||||
/*
|
||||
* utility functions
|
||||
*/
|
||||
int fstUtilityBinToEscConvertedLen(const unsigned char *s, int len); /* used for mallocs for fstUtilityBinToEsc() */
|
||||
int fstUtilityBinToEsc(unsigned char *d, const unsigned char *s, int len);
|
||||
int fstUtilityEscToBin(unsigned char *d, unsigned char *s, int len);
|
||||
struct fstETab *fstUtilityExtractEnumTableFromString(const char *s);
|
||||
void fstUtilityFreeEnumTable(struct fstETab *etab); /* must use to free fstETab properly */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
LZ4 - Fast LZ compression algorithm
|
||||
Header File
|
||||
Copyright (C) 2011-2015, Yann Collet.
|
||||
|
||||
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
You can contact the author at :
|
||||
- LZ4 source repository : https://github.com/Cyan4973/lz4
|
||||
- LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* lz4.h provides block compression functions, and gives full buffer control to programmer.
|
||||
* If you need to generate inter-operable compressed data (respecting LZ4 frame specification),
|
||||
* and can let the library handle its own memory, please use lz4frame.h instead.
|
||||
*/
|
||||
|
||||
/**************************************
|
||||
* Version
|
||||
**************************************/
|
||||
#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
|
||||
#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */
|
||||
#define LZ4_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */
|
||||
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR * 100 * 100 + LZ4_VERSION_MINOR * 100 + LZ4_VERSION_RELEASE)
|
||||
int LZ4_versionNumber(void);
|
||||
|
||||
/**************************************
|
||||
* Tuning parameter
|
||||
**************************************/
|
||||
/*
|
||||
* LZ4_MEMORY_USAGE :
|
||||
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
|
||||
* Increasing memory usage improves compression ratio
|
||||
* Reduced memory usage can improve speed, due to cache effect
|
||||
* Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
|
||||
*/
|
||||
#define LZ4_MEMORY_USAGE 14
|
||||
|
||||
/**************************************
|
||||
* Simple Functions
|
||||
**************************************/
|
||||
|
||||
int LZ4_compress_default(const char *source, char *dest, int sourceSize, int maxDestSize);
|
||||
int LZ4_decompress_safe(const char *source, char *dest, int compressedSize, int maxDecompressedSize);
|
||||
|
||||
/*
|
||||
LZ4_compress_default() :
|
||||
Compresses 'sourceSize' bytes from buffer 'source'
|
||||
into already allocated 'dest' buffer of size 'maxDestSize'.
|
||||
Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize).
|
||||
It also runs faster, so it's a recommended setting.
|
||||
If the function cannot compress 'source' into a more limited 'dest' budget,
|
||||
compression stops *immediately*, and the function result is zero.
|
||||
As a consequence, 'dest' content is not valid.
|
||||
This function never writes outside 'dest' buffer, nor read outside 'source' buffer.
|
||||
sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE
|
||||
maxDestSize : full or partial size of buffer 'dest' (which must be already allocated)
|
||||
return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize)
|
||||
or 0 if compression fails
|
||||
|
||||
LZ4_decompress_safe() :
|
||||
compressedSize : is the precise full size of the compressed block.
|
||||
maxDecompressedSize : is the size of destination buffer, which must be already allocated.
|
||||
return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize)
|
||||
If destination buffer is not large enough, decoding will stop and output an error code (<0).
|
||||
If the source stream is detected malformed, the function will stop decoding and return a negative result.
|
||||
This function is protected against buffer overflow exploits, including malicious data packets.
|
||||
It never writes outside output buffer, nor reads outside input buffer.
|
||||
*/
|
||||
|
||||
/**************************************
|
||||
* Advanced Functions
|
||||
**************************************/
|
||||
#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
|
||||
#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize) / 255) + 16)
|
||||
|
||||
/*
|
||||
LZ4_compressBound() :
|
||||
Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
|
||||
This function is primarily useful for memory allocation purposes (destination buffer size).
|
||||
Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
|
||||
Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize)
|
||||
inputSize : max supported value is LZ4_MAX_INPUT_SIZE
|
||||
return : maximum output size in a "worst case" scenario
|
||||
or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE)
|
||||
*/
|
||||
int LZ4_compressBound(int inputSize);
|
||||
|
||||
/*
|
||||
LZ4_compress_fast() :
|
||||
Same as LZ4_compress_default(), but allows to select an "acceleration" factor.
|
||||
The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
|
||||
It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
|
||||
An acceleration value of "1" is the same as regular LZ4_compress_default()
|
||||
Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1.
|
||||
*/
|
||||
int LZ4_compress_fast(const char *source, char *dest, int sourceSize, int maxDestSize, int acceleration);
|
||||
|
||||
/*
|
||||
LZ4_compress_fast_extState() :
|
||||
Same compression function, just using an externally allocated memory space to store compression state.
|
||||
Use LZ4_sizeofState() to know how much memory must be allocated,
|
||||
and allocate it on 8-bytes boundaries (using malloc() typically).
|
||||
Then, provide it as 'void* state' to compression function.
|
||||
*/
|
||||
int LZ4_sizeofState(void);
|
||||
int LZ4_compress_fast_extState(void *state, const char *source, char *dest, int inputSize, int maxDestSize,
|
||||
int acceleration);
|
||||
|
||||
/*
|
||||
LZ4_compress_destSize() :
|
||||
Reverse the logic, by compressing as much data as possible from 'source' buffer
|
||||
into already allocated buffer 'dest' of size 'targetDestSize'.
|
||||
This function either compresses the entire 'source' content into 'dest' if it's large enough,
|
||||
or fill 'dest' buffer completely with as much data as possible from 'source'.
|
||||
*sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'.
|
||||
New value is necessarily <= old value.
|
||||
return : Nb bytes written into 'dest' (necessarily <= targetDestSize)
|
||||
or 0 if compression fails
|
||||
*/
|
||||
int LZ4_compress_destSize(const char *source, char *dest, int *sourceSizePtr, int targetDestSize);
|
||||
|
||||
/*
|
||||
LZ4_decompress_fast() :
|
||||
originalSize : is the original and therefore uncompressed size
|
||||
return : the number of bytes read from the source buffer (in other words, the compressed size)
|
||||
If the source stream is detected malformed, the function will stop decoding and return a negative result.
|
||||
Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes.
|
||||
note : This function fully respect memory boundaries for properly formed compressed data.
|
||||
It is a bit faster than LZ4_decompress_safe().
|
||||
However, it does not provide any protection against intentionally modified data stream (malicious input).
|
||||
Use this function in trusted environment only (data to decode comes from a trusted source).
|
||||
*/
|
||||
int LZ4_decompress_fast(const char *source, char *dest, int originalSize);
|
||||
|
||||
/*
|
||||
LZ4_decompress_safe_partial() :
|
||||
This function decompress a compressed block of size 'compressedSize' at position 'source'
|
||||
into destination buffer 'dest' of size 'maxDecompressedSize'.
|
||||
The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached,
|
||||
reducing decompression time.
|
||||
return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize)
|
||||
Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller.
|
||||
Always control how many bytes were decoded.
|
||||
If the source stream is detected malformed, the function will stop decoding and return a negative result.
|
||||
This function never writes outside of output buffer, and never reads outside of input buffer. It is
|
||||
therefore protected against malicious data packets
|
||||
*/
|
||||
int LZ4_decompress_safe_partial(const char *source, char *dest, int compressedSize, int targetOutputSize,
|
||||
int maxDecompressedSize);
|
||||
|
||||
/***********************************************
|
||||
* Streaming Compression Functions
|
||||
***********************************************/
|
||||
#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE - 3)) + 4)
|
||||
#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long))
|
||||
/*
|
||||
* LZ4_stream_t
|
||||
* information structure to track an LZ4 stream.
|
||||
* important : init this structure content before first use !
|
||||
* note : only allocated directly the structure if you are statically linking LZ4
|
||||
* If you are using liblz4 as a DLL, please use below construction methods instead.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
long long table[LZ4_STREAMSIZE_U64];
|
||||
} LZ4_stream_t;
|
||||
|
||||
/*
|
||||
* LZ4_resetStream
|
||||
* Use this function to init an allocated LZ4_stream_t structure
|
||||
*/
|
||||
void LZ4_resetStream(LZ4_stream_t *streamPtr);
|
||||
|
||||
/*
|
||||
* LZ4_createStream will allocate and initialize an LZ4_stream_t structure
|
||||
* LZ4_freeStream releases its memory.
|
||||
* In the context of a DLL (liblz4), please use these methods rather than the static struct.
|
||||
* They are more future proof, in case of a change of LZ4_stream_t size.
|
||||
*/
|
||||
LZ4_stream_t *LZ4_createStream(void);
|
||||
int LZ4_freeStream(LZ4_stream_t *streamPtr);
|
||||
|
||||
/*
|
||||
* LZ4_loadDict
|
||||
* Use this function to load a static dictionary into LZ4_stream.
|
||||
* Any previous data will be forgotten, only 'dictionary' will remain in memory.
|
||||
* Loading a size of 0 is allowed.
|
||||
* Return : dictionary size, in bytes (necessarily <= 64 KB)
|
||||
*/
|
||||
int LZ4_loadDict(LZ4_stream_t *streamPtr, const char *dictionary, int dictSize);
|
||||
|
||||
/*
|
||||
* LZ4_compress_fast_continue
|
||||
* Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression
|
||||
* ratio. Important : Previous data blocks are assumed to still be present and unmodified ! 'dst' buffer must be already
|
||||
* allocated. If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. If
|
||||
* not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero.
|
||||
*/
|
||||
int LZ4_compress_fast_continue(LZ4_stream_t *streamPtr, const char *src, char *dst, int srcSize, int maxDstSize,
|
||||
int acceleration);
|
||||
|
||||
/*
|
||||
* LZ4_saveDict
|
||||
* If previously compressed data block is not guaranteed to remain available at its memory location
|
||||
* save it into a safer place (char* safeBuffer)
|
||||
* Note : you don't need to call LZ4_loadDict() afterwards,
|
||||
* dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue()
|
||||
* Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error
|
||||
*/
|
||||
int LZ4_saveDict(LZ4_stream_t *streamPtr, char *safeBuffer, int dictSize);
|
||||
|
||||
/************************************************
|
||||
* Streaming Decompression Functions
|
||||
************************************************/
|
||||
|
||||
#define LZ4_STREAMDECODESIZE_U64 4
|
||||
#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
|
||||
typedef struct
|
||||
{
|
||||
unsigned long long table[LZ4_STREAMDECODESIZE_U64];
|
||||
} LZ4_streamDecode_t;
|
||||
/*
|
||||
* LZ4_streamDecode_t
|
||||
* information structure to track an LZ4 stream.
|
||||
* init this structure content using LZ4_setStreamDecode or memset() before first use !
|
||||
*
|
||||
* In the context of a DLL (liblz4) please prefer usage of construction methods below.
|
||||
* They are more future proof, in case of a change of LZ4_streamDecode_t size in the future.
|
||||
* LZ4_createStreamDecode will allocate and initialize an LZ4_streamDecode_t structure
|
||||
* LZ4_freeStreamDecode releases its memory.
|
||||
*/
|
||||
LZ4_streamDecode_t *LZ4_createStreamDecode(void);
|
||||
int LZ4_freeStreamDecode(LZ4_streamDecode_t *LZ4_stream);
|
||||
|
||||
/*
|
||||
* LZ4_setStreamDecode
|
||||
* Use this function to instruct where to find the dictionary.
|
||||
* Setting a size of 0 is allowed (same effect as reset).
|
||||
* Return : 1 if OK, 0 if error
|
||||
*/
|
||||
int LZ4_setStreamDecode(LZ4_streamDecode_t *LZ4_streamDecode, const char *dictionary, int dictSize);
|
||||
|
||||
/*
|
||||
*_continue() :
|
||||
These decoding functions allow decompression of multiple blocks in "streaming" mode.
|
||||
Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB)
|
||||
In the case of a ring buffers, decoding buffer must be either :
|
||||
- Exactly same size as encoding buffer, with same update rule (block boundaries at same positions)
|
||||
In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB).
|
||||
- Larger than encoding buffer, by a minimum of maxBlockSize more bytes.
|
||||
maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block.
|
||||
In which case, encoding and decoding buffers do not need to be synchronized,
|
||||
and encoding ring buffer can have any size, including small ones ( < 64 KB).
|
||||
- _At least_ 64 KB + 8 bytes + maxBlockSize.
|
||||
In which case, encoding and decoding buffers do not need to be synchronized,
|
||||
and encoding ring buffer can have any size, including larger than decoding buffer.
|
||||
Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer,
|
||||
and indicate where it is saved using LZ4_setStreamDecode()
|
||||
*/
|
||||
int LZ4_decompress_safe_continue(LZ4_streamDecode_t *LZ4_streamDecode, const char *source, char *dest,
|
||||
int compressedSize, int maxDecompressedSize);
|
||||
int LZ4_decompress_fast_continue(LZ4_streamDecode_t *LZ4_streamDecode, const char *source, char *dest,
|
||||
int originalSize);
|
||||
|
||||
/*
|
||||
Advanced decoding functions :
|
||||
*_usingDict() :
|
||||
These decoding functions work the same as
|
||||
a combination of LZ4_setStreamDecode() followed by LZ4_decompress_x_continue()
|
||||
They are stand-alone. They don't need nor update an LZ4_streamDecode_t structure.
|
||||
*/
|
||||
int LZ4_decompress_safe_usingDict(const char *source, char *dest, int compressedSize, int maxDecompressedSize,
|
||||
const char *dictStart, int dictSize);
|
||||
int LZ4_decompress_fast_usingDict(const char *source, char *dest, int originalSize, const char *dictStart,
|
||||
int dictSize);
|
||||
|
||||
/**************************************
|
||||
* Obsolete Functions
|
||||
**************************************/
|
||||
/* Deprecate Warnings */
|
||||
/* Should these warnings messages be a problem,
|
||||
it is generally possible to disable them,
|
||||
with -Wno-deprecated-declarations for gcc
|
||||
or _CRT_SECURE_NO_WARNINGS in Visual for example.
|
||||
You can also define LZ4_DEPRECATE_WARNING_DEFBLOCK. */
|
||||
#ifndef LZ4_DEPRECATE_WARNING_DEFBLOCK
|
||||
#define LZ4_DEPRECATE_WARNING_DEFBLOCK
|
||||
#define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||
#if (LZ4_GCC_VERSION >= 405) || defined(__clang__)
|
||||
#define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
|
||||
#elif (LZ4_GCC_VERSION >= 301)
|
||||
#define LZ4_DEPRECATED(message) __attribute__((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
#define LZ4_DEPRECATED(message) __declspec(deprecated(message))
|
||||
#else
|
||||
#pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler")
|
||||
#define LZ4_DEPRECATED(message)
|
||||
#endif
|
||||
#endif /* LZ4_DEPRECATE_WARNING_DEFBLOCK */
|
||||
|
||||
/* Obsolete compression functions */
|
||||
/* These functions are planned to start generate warnings by r131 approximately */
|
||||
int LZ4_compress(const char *source, char *dest, int sourceSize);
|
||||
int LZ4_compress_limitedOutput(const char *source, char *dest, int sourceSize, int maxOutputSize);
|
||||
int LZ4_compress_withState(void *state, const char *source, char *dest, int inputSize);
|
||||
int LZ4_compress_limitedOutput_withState(void *state, const char *source, char *dest, int inputSize, int maxOutputSize);
|
||||
int LZ4_compress_continue(LZ4_stream_t *LZ4_streamPtr, const char *source, char *dest, int inputSize);
|
||||
int LZ4_compress_limitedOutput_continue(LZ4_stream_t *LZ4_streamPtr, const char *source, char *dest, int inputSize,
|
||||
int maxOutputSize);
|
||||
|
||||
/* Obsolete decompression functions */
|
||||
/* These function names are completely deprecated and must no longer be used.
|
||||
They are only provided here for compatibility with older programs.
|
||||
- LZ4_uncompress is the same as LZ4_decompress_fast
|
||||
- LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe
|
||||
These function prototypes are now disabled; uncomment them only if you really need them.
|
||||
It is highly recommended to stop using these prototypes and migrate to maintained ones */
|
||||
/* int LZ4_uncompress (const char* source, char* dest, int outputSize); */
|
||||
/* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */
|
||||
|
||||
/* Obsolete streaming functions; use new streaming interface whenever possible */
|
||||
LZ4_DEPRECATED("use LZ4_createStream() instead") void *LZ4_create(char *inputBuffer);
|
||||
LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void);
|
||||
LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void *state, char *inputBuffer);
|
||||
LZ4_DEPRECATED("use LZ4_saveDict() instead") char *LZ4_slideInputBuffer(void *state);
|
||||
|
||||
/* Obsolete streaming decoding functions */
|
||||
LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead")
|
||||
int LZ4_decompress_safe_withPrefix64k(const char *src, char *dst, int compressedSize, int maxDstSize);
|
||||
LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead")
|
||||
int LZ4_decompress_fast_withPrefix64k(const char *src, char *dst, int originalSize);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
|
@ -7,11 +7,20 @@ gitsha="$3"
|
|||
|
||||
rm -rf YosysVS-Tpl-v2.zip YosysVS
|
||||
wget https://yosyshq.net/yosys/nogit/YosysVS-Tpl-v2.zip
|
||||
wget https://zlib.net/zlib-1.2.11.tar.gz
|
||||
|
||||
unzip YosysVS-Tpl-v2.zip
|
||||
rm -f YosysVS-Tpl-v2.zip
|
||||
mv YosysVS "$vcxsrc"
|
||||
tar xvfz zlib-1.2.11.tar.gz
|
||||
|
||||
mv YosysVS "$vcxsrc"
|
||||
mkdir -p "$vcxsrc"/yosys
|
||||
mkdir -p "$vcxsrc"/yosys/libs/zlib
|
||||
mv zlib-1.2.11/* "$vcxsrc"/yosys/libs/zlib/.
|
||||
rm -rf zlib-1.2.11
|
||||
pushd "$vcxsrc"/yosys
|
||||
ls libs/zlib/*.c | sed 's,.*:,,; s,//*,/,g; s,/[^/]*/\.\./,/,g; y, \\,\n\n,;' | grep '^[^/]' >> ../../srcfiles.txt
|
||||
popd
|
||||
{
|
||||
n=$(grep -B999 '<ItemGroup>' "$vcxsrc"/YosysVS/YosysVS.vcxproj | wc -l)
|
||||
head -n$n "$vcxsrc"/YosysVS/YosysVS.vcxproj
|
||||
|
|
|
@ -21,12 +21,49 @@
|
|||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/mem.h"
|
||||
#include "kernel/fstdata.h"
|
||||
|
||||
#include <ctime>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
enum class SimulationMode {
|
||||
sim,
|
||||
cmp,
|
||||
gold,
|
||||
gate,
|
||||
};
|
||||
|
||||
static const std::map<std::string, int> g_units =
|
||||
{
|
||||
{ "", -9 }, // default is ns
|
||||
{ "s", 0 },
|
||||
{ "ms", -3 },
|
||||
{ "us", -6 },
|
||||
{ "ns", -9 },
|
||||
{ "ps", -12 },
|
||||
{ "fs", -15 },
|
||||
{ "as", -18 },
|
||||
{ "zs", -21 },
|
||||
};
|
||||
|
||||
static double stringToTime(std::string str)
|
||||
{
|
||||
if (str=="END") return -1;
|
||||
|
||||
char *endptr;
|
||||
long value = strtol(str.c_str(), &endptr, 10);
|
||||
|
||||
if (g_units.find(endptr)==g_units.end())
|
||||
log_error("Cannot parse '%s', bad unit '%s'\n", str.c_str(), endptr);
|
||||
|
||||
if (value < 0)
|
||||
log_error("Time value '%s' must be positive\n", str.c_str());
|
||||
|
||||
return value * pow(10.0, g_units.at(endptr));
|
||||
}
|
||||
|
||||
struct SimShared
|
||||
{
|
||||
bool debug = false;
|
||||
|
@ -34,6 +71,11 @@ struct SimShared
|
|||
bool writeback = false;
|
||||
bool zinit = false;
|
||||
int rstlen = 1;
|
||||
FstData *fst = nullptr;
|
||||
double start_time = 0;
|
||||
double stop_time = -1;
|
||||
SimulationMode sim_mode = SimulationMode::sim;
|
||||
bool cycles_set = false;
|
||||
};
|
||||
|
||||
void zinit(State &v)
|
||||
|
@ -51,7 +93,8 @@ void zinit(Const &v)
|
|||
struct SimInstance
|
||||
{
|
||||
SimShared *shared;
|
||||
|
||||
|
||||
std::string scope;
|
||||
Module *module;
|
||||
Cell *instance;
|
||||
|
||||
|
@ -92,9 +135,11 @@ struct SimInstance
|
|||
std::vector<Mem> memories;
|
||||
|
||||
dict<Wire*, pair<int, Const>> vcd_database;
|
||||
dict<Wire*, pair<fstHandle, Const>> fst_database;
|
||||
dict<Wire*, fstHandle> fst_handles;
|
||||
|
||||
SimInstance(SimShared *shared, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) :
|
||||
shared(shared), module(module), instance(instance), parent(parent), sigmap(module)
|
||||
SimInstance(SimShared *shared, std::string scope, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) :
|
||||
shared(shared), scope(scope), module(module), instance(instance), parent(parent), sigmap(module)
|
||||
{
|
||||
log_assert(module);
|
||||
|
||||
|
@ -116,6 +161,13 @@ struct SimInstance
|
|||
}
|
||||
}
|
||||
|
||||
if ((shared->fst) && !(shared->hide_internal && wire->name[0] == '$')) {
|
||||
fstHandle id = shared->fst->getHandle(scope + "." + RTLIL::unescape_id(wire->name));
|
||||
if (id==0 && wire->name.isPublic())
|
||||
log_warning("Unable to found wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(wire->name)).c_str());
|
||||
fst_handles[wire] = id;
|
||||
}
|
||||
|
||||
if (wire->attributes.count(ID::init)) {
|
||||
Const initval = wire->attributes.at(ID::init);
|
||||
for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++)
|
||||
|
@ -144,7 +196,7 @@ struct SimInstance
|
|||
Module *mod = module->design->module(cell->type);
|
||||
|
||||
if (mod != nullptr) {
|
||||
dirty_children.insert(new SimInstance(shared, mod, cell, this));
|
||||
dirty_children.insert(new SimInstance(shared, scope + "." + RTLIL::unescape_id(cell->name), mod, cell, this));
|
||||
}
|
||||
|
||||
for (auto &port : cell->connections()) {
|
||||
|
@ -622,14 +674,125 @@ struct SimInstance
|
|||
for (auto child : children)
|
||||
child.second->write_vcd_step(f);
|
||||
}
|
||||
|
||||
void write_fst_header(struct fstContext *f)
|
||||
{
|
||||
fstWriterSetScope(f, FST_ST_VCD_MODULE, stringf("%s",log_id(name())).c_str(), nullptr);
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
if (shared->hide_internal && wire->name[0] == '$')
|
||||
continue;
|
||||
|
||||
fstHandle id = fstWriterCreateVar(f, FST_VT_VCD_WIRE, FST_VD_IMPLICIT, GetSize(wire),
|
||||
stringf("%s%s", wire->name[0] == '$' ? "\\" : "", log_id(wire)).c_str(), 0);
|
||||
fst_database[wire] = make_pair(id, Const());
|
||||
}
|
||||
|
||||
for (auto child : children)
|
||||
child.second->write_fst_header(f);
|
||||
|
||||
fstWriterSetUpscope(f);
|
||||
}
|
||||
|
||||
void write_fst_step(struct fstContext *f)
|
||||
{
|
||||
for (auto &it : fst_database)
|
||||
{
|
||||
Wire *wire = it.first;
|
||||
Const value = get_state(wire);
|
||||
fstHandle id = it.second.first;
|
||||
|
||||
if (it.second.second == value)
|
||||
continue;
|
||||
|
||||
it.second.second = value;
|
||||
std::stringstream ss;
|
||||
for (int i = GetSize(value)-1; i >= 0; i--) {
|
||||
switch (value[i]) {
|
||||
case State::S0: ss << "0"; break;
|
||||
case State::S1: ss << "1"; break;
|
||||
case State::Sx: ss << "x"; break;
|
||||
default: ss << "z";
|
||||
}
|
||||
}
|
||||
fstWriterEmitValueChange(f, id, ss.str().c_str());
|
||||
}
|
||||
|
||||
for (auto child : children)
|
||||
child.second->write_fst_step(f);
|
||||
}
|
||||
|
||||
void setInitState(uint64_t time)
|
||||
{
|
||||
for (auto &it : ff_database)
|
||||
{
|
||||
Cell *cell = it.first;
|
||||
|
||||
SigSpec qsig = cell->getPort(ID::Q);
|
||||
if (qsig.is_wire()) {
|
||||
IdString name = qsig.as_wire()->name;
|
||||
fstHandle id = shared->fst->getHandle(scope + "." + RTLIL::unescape_id(name));
|
||||
if (id==0 && name.isPublic())
|
||||
log_warning("Unable to found wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(name)).c_str());
|
||||
if (id!=0) {
|
||||
Const fst_val = Const::from_string(shared->fst->valueAt(id, time));
|
||||
set_state(qsig, fst_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto child : children)
|
||||
child.second->setInitState(time);
|
||||
}
|
||||
|
||||
bool checkSignals(uint64_t time)
|
||||
{
|
||||
bool retVal = false;
|
||||
for(auto &item : fst_handles) {
|
||||
if (item.second==0) continue; // Ignore signals not found
|
||||
Const fst_val = Const::from_string(shared->fst->valueAt(item.second, time));
|
||||
Const sim_val = get_state(item.first);
|
||||
if (sim_val.size()!=fst_val.size())
|
||||
log_error("Signal '%s' size is different in gold and gate.\n", log_id(item.first));
|
||||
if (shared->sim_mode == SimulationMode::sim) {
|
||||
// No checks performed when using stimulus
|
||||
} else if (shared->sim_mode == SimulationMode::gate && !fst_val.is_fully_def()) { // FST data contains X
|
||||
for(int i=0;i<fst_val.size();i++) {
|
||||
if (fst_val[i]!=State::Sx && fst_val[i]!=sim_val[i]) {
|
||||
log_warning("Signal '%s' in file %s in simulation %s\n", log_id(item.first), log_signal(fst_val), log_signal(sim_val));
|
||||
retVal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (shared->sim_mode == SimulationMode::gold && !sim_val.is_fully_def()) { // sim data contains X
|
||||
for(int i=0;i<sim_val.size();i++) {
|
||||
if (sim_val[i]!=State::Sx && fst_val[i]!=sim_val[i]) {
|
||||
log_warning("Signal '%s' in file %s in simulation %s\n", log_id(item.first), log_signal(fst_val), log_signal(sim_val));
|
||||
retVal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (fst_val!=sim_val) {
|
||||
log_warning("Signal '%s' in file %s in simulation '%s'\n", log_id(item.first), log_signal(fst_val), log_signal(sim_val));
|
||||
retVal = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto child : children)
|
||||
retVal |= child.second->checkSignals(time);
|
||||
return retVal;
|
||||
}
|
||||
};
|
||||
|
||||
struct SimWorker : SimShared
|
||||
{
|
||||
SimInstance *top = nullptr;
|
||||
std::ofstream vcdfile;
|
||||
struct fstContext *fstfile = nullptr;
|
||||
pool<IdString> clock, clockn, reset, resetn;
|
||||
std::string timescale;
|
||||
std::string sim_filename;
|
||||
std::string scope;
|
||||
|
||||
~SimWorker()
|
||||
{
|
||||
|
@ -638,9 +801,6 @@ struct SimWorker : SimShared
|
|||
|
||||
void write_vcd_header()
|
||||
{
|
||||
if (!vcdfile.is_open())
|
||||
return;
|
||||
|
||||
vcdfile << stringf("$version %s $end\n", yosys_version_str);
|
||||
|
||||
std::time_t t = std::time(nullptr);
|
||||
|
@ -660,13 +820,53 @@ struct SimWorker : SimShared
|
|||
|
||||
void write_vcd_step(int t)
|
||||
{
|
||||
if (!vcdfile.is_open())
|
||||
return;
|
||||
|
||||
vcdfile << stringf("#%d\n", t);
|
||||
top->write_vcd_step(vcdfile);
|
||||
}
|
||||
|
||||
void write_fst_header()
|
||||
{
|
||||
std::time_t t = std::time(nullptr);
|
||||
fstWriterSetDate(fstfile, asctime(std::localtime(&t)));
|
||||
fstWriterSetVersion(fstfile, yosys_version_str);
|
||||
if (!timescale.empty())
|
||||
fstWriterSetTimescaleFromString(fstfile, timescale.c_str());
|
||||
|
||||
fstWriterSetPackType(fstfile, FST_WR_PT_FASTLZ);
|
||||
fstWriterSetRepackOnClose(fstfile, 1);
|
||||
|
||||
top->write_fst_header(fstfile);
|
||||
}
|
||||
|
||||
void write_fst_step(int t)
|
||||
{
|
||||
fstWriterEmitTimeChange(fstfile, t);
|
||||
|
||||
top->write_fst_step(fstfile);
|
||||
}
|
||||
|
||||
void write_output_header()
|
||||
{
|
||||
if (vcdfile.is_open())
|
||||
write_vcd_header();
|
||||
if (fstfile)
|
||||
write_fst_header();
|
||||
}
|
||||
|
||||
void write_output_step(int t)
|
||||
{
|
||||
if (vcdfile.is_open())
|
||||
write_vcd_step(t);
|
||||
if (fstfile)
|
||||
write_fst_step(t);
|
||||
}
|
||||
|
||||
void write_output_end()
|
||||
{
|
||||
if (fstfile)
|
||||
fstWriterClose(fstfile);
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
while (1)
|
||||
|
@ -705,7 +905,7 @@ struct SimWorker : SimShared
|
|||
void run(Module *topmod, int numcycles)
|
||||
{
|
||||
log_assert(top == nullptr);
|
||||
top = new SimInstance(this, topmod);
|
||||
top = new SimInstance(this, scope, topmod);
|
||||
|
||||
if (debug)
|
||||
log("\n===== 0 =====\n");
|
||||
|
@ -720,24 +920,25 @@ struct SimWorker : SimShared
|
|||
|
||||
update();
|
||||
|
||||
write_vcd_header();
|
||||
write_vcd_step(0);
|
||||
write_output_header();
|
||||
write_output_step(0);
|
||||
|
||||
for (int cycle = 0; cycle < numcycles; cycle++)
|
||||
{
|
||||
if (debug)
|
||||
log("\n===== %d =====\n", 10*cycle + 5);
|
||||
|
||||
else
|
||||
log("Simulating cycle %d.\n", (cycle*2)+1);
|
||||
set_inports(clock, State::S0);
|
||||
set_inports(clockn, State::S1);
|
||||
|
||||
update();
|
||||
write_vcd_step(10*cycle + 5);
|
||||
write_output_step(10*cycle + 5);
|
||||
|
||||
if (debug)
|
||||
log("\n===== %d =====\n", 10*cycle + 10);
|
||||
else
|
||||
log("Simulating cycle %d.\n", cycle+1);
|
||||
log("Simulating cycle %d.\n", (cycle*2)+2);
|
||||
|
||||
set_inports(clock, State::S1);
|
||||
set_inports(clockn, State::S0);
|
||||
|
@ -748,11 +949,132 @@ struct SimWorker : SimShared
|
|||
}
|
||||
|
||||
update();
|
||||
write_vcd_step(10*cycle + 10);
|
||||
write_output_step(10*cycle + 10);
|
||||
}
|
||||
|
||||
write_vcd_step(10*numcycles + 2);
|
||||
write_output_step(10*numcycles + 2);
|
||||
|
||||
write_output_end();
|
||||
|
||||
if (writeback) {
|
||||
pool<Module*> wbmods;
|
||||
top->writeback(wbmods);
|
||||
}
|
||||
}
|
||||
|
||||
void run_cosim(Module *topmod, int numcycles)
|
||||
{
|
||||
log_assert(top == nullptr);
|
||||
fst = new FstData(sim_filename);
|
||||
|
||||
if (scope.empty())
|
||||
log_error("Scope must be defined for co-simulation.\n");
|
||||
|
||||
top = new SimInstance(this, scope, topmod);
|
||||
|
||||
std::vector<fstHandle> fst_clock;
|
||||
|
||||
for (auto portname : clock)
|
||||
{
|
||||
Wire *w = topmod->wire(portname);
|
||||
if (!w)
|
||||
log_error("Can't find port %s on module %s.\n", log_id(portname), log_id(top->module));
|
||||
if (!w->port_input)
|
||||
log_error("Clock port %s on module %s is not input.\n", log_id(portname), log_id(top->module));
|
||||
fstHandle id = fst->getHandle(scope + "." + RTLIL::unescape_id(portname));
|
||||
if (id==0)
|
||||
log_error("Can't find port %s.%s in FST.\n", scope.c_str(), log_id(portname));
|
||||
fst_clock.push_back(id);
|
||||
}
|
||||
for (auto portname : clockn)
|
||||
{
|
||||
Wire *w = topmod->wire(portname);
|
||||
if (!w)
|
||||
log_error("Can't find port %s on module %s.\n", log_id(portname), log_id(top->module));
|
||||
if (!w->port_input)
|
||||
log_error("Clock port %s on module %s is not input.\n", log_id(portname), log_id(top->module));
|
||||
fstHandle id = fst->getHandle(scope + "." + RTLIL::unescape_id(portname));
|
||||
if (id==0)
|
||||
log_error("Can't find port %s.%s in FST.\n", scope.c_str(), log_id(portname));
|
||||
fst_clock.push_back(id);
|
||||
}
|
||||
if (fst_clock.size()==0)
|
||||
log_error("No clock signals defined for input file\n");
|
||||
|
||||
SigMap sigmap(topmod);
|
||||
std::map<Wire*,fstHandle> inputs;
|
||||
|
||||
for (auto wire : topmod->wires()) {
|
||||
if (wire->port_input) {
|
||||
fstHandle id = fst->getHandle(scope + "." + RTLIL::unescape_id(wire->name));
|
||||
if (id==0)
|
||||
log_error("Unable to find required '%s' signal in file\n",(scope + "." + RTLIL::unescape_id(wire->name)).c_str());
|
||||
inputs[wire] = id;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t startCount = 0;
|
||||
uint64_t stopCount = 0;
|
||||
if (start_time==0) {
|
||||
if (start_time < fst->getStartTime())
|
||||
log_warning("Start time is before simulation file start time\n");
|
||||
startCount = fst->getStartTime();
|
||||
} else if (start_time==-1)
|
||||
startCount = fst->getEndTime();
|
||||
else {
|
||||
startCount = start_time / fst->getTimescale();
|
||||
if (startCount > fst->getEndTime()) {
|
||||
startCount = fst->getEndTime();
|
||||
log_warning("Start time is after simulation file end time\n");
|
||||
}
|
||||
}
|
||||
if (stop_time==0) {
|
||||
if (stop_time < fst->getStartTime())
|
||||
log_warning("Stop time is before simulation file start time\n");
|
||||
stopCount = fst->getStartTime();
|
||||
} else if (stop_time==-1)
|
||||
stopCount = fst->getEndTime();
|
||||
else {
|
||||
stopCount = stop_time / fst->getTimescale();
|
||||
if (stopCount > fst->getEndTime()) {
|
||||
stopCount = fst->getEndTime();
|
||||
log_warning("Stop time is after simulation file end time\n");
|
||||
}
|
||||
}
|
||||
if (stopCount<startCount) {
|
||||
log_error("Stop time is before start time\n");
|
||||
}
|
||||
auto samples = fst->getAllEdges(fst_clock, startCount, stopCount);
|
||||
|
||||
// Limit to number of cycles if provided
|
||||
if (cycles_set && ((size_t)(numcycles *2) < samples.size()))
|
||||
samples.erase(samples.begin() + (numcycles*2), samples.end());
|
||||
|
||||
// Add setup time (start time)
|
||||
if (samples.empty() || samples.front()!=startCount)
|
||||
samples.insert(samples.begin(), startCount);
|
||||
|
||||
fst->reconstructAllAtTimes(samples);
|
||||
bool initial = true;
|
||||
int cycle = 0;
|
||||
log("Co-simulation from %lu%s to %lu%s\n", (unsigned long)startCount, fst->getTimescaleString(), (unsigned long)stopCount, fst->getTimescaleString());
|
||||
for(auto &time : samples) {
|
||||
log("Co-simulating cycle %d [%lu%s].\n", cycle, (unsigned long)time, fst->getTimescaleString());
|
||||
for(auto &item : inputs) {
|
||||
std::string v = fst->valueAt(item.second, time);
|
||||
top->set_state(item.first, Const::from_string(v));
|
||||
}
|
||||
if (initial) {
|
||||
top->setInitState(time);
|
||||
initial = false;
|
||||
}
|
||||
update();
|
||||
|
||||
bool status = top->checkSignals(time);
|
||||
if (status)
|
||||
log_error("Signal difference\n");
|
||||
cycle++;
|
||||
}
|
||||
if (writeback) {
|
||||
pool<Module*> wbmods;
|
||||
top->writeback(wbmods);
|
||||
|
@ -773,6 +1095,9 @@ struct SimPass : public Pass {
|
|||
log(" -vcd <filename>\n");
|
||||
log(" write the simulation results to the given VCD file\n");
|
||||
log("\n");
|
||||
log(" -fst <filename>\n");
|
||||
log(" write the simulation results to the given FST file\n");
|
||||
log("\n");
|
||||
log(" -clock <portname>\n");
|
||||
log(" name of top-level clock input\n");
|
||||
log("\n");
|
||||
|
@ -795,14 +1120,41 @@ struct SimPass : public Pass {
|
|||
log(" include the specified timescale declaration in the vcd\n");
|
||||
log("\n");
|
||||
log(" -n <integer>\n");
|
||||
log(" number of cycles to simulate (default: 20)\n");
|
||||
log(" number of clock cycles to simulate (default: 20)\n");
|
||||
log("\n");
|
||||
log(" -a\n");
|
||||
log(" include all nets in VCD output, not just those with public names\n");
|
||||
log(" use all nets in VCD/FST operations, not just those with public names\n");
|
||||
log("\n");
|
||||
log(" -w\n");
|
||||
log(" writeback mode: use final simulation state as new init state\n");
|
||||
log("\n");
|
||||
log(" -r\n");
|
||||
log(" read simulation results file (file formats supported: FST)\n");
|
||||
log("\n");
|
||||
log(" -scope\n");
|
||||
log(" scope of simulation top model\n");
|
||||
log("\n");
|
||||
log(" -at <time>\n");
|
||||
log(" sets start and stop time\n");
|
||||
log("\n");
|
||||
log(" -start <time>\n");
|
||||
log(" start co-simulation in arbitary time (default 0)\n");
|
||||
log("\n");
|
||||
log(" -stop <time>\n");
|
||||
log(" stop co-simulation in arbitary time (default END)\n");
|
||||
log("\n");
|
||||
log(" -sim\n");
|
||||
log(" simulation with stimulus from FST (default)\n");
|
||||
log("\n");
|
||||
log(" -sim-cmp\n");
|
||||
log(" co-simulation expect exact match\n");
|
||||
log("\n");
|
||||
log(" -sim-gold\n");
|
||||
log(" co-simulation, x in simulation can match any value in FST\n");
|
||||
log("\n");
|
||||
log(" -sim-gate\n");
|
||||
log(" co-simulation, x in FST can match any value in simulation\n");
|
||||
log("\n");
|
||||
log(" -d\n");
|
||||
log(" enable debug output\n");
|
||||
log("\n");
|
||||
|
@ -811,6 +1163,7 @@ struct SimPass : public Pass {
|
|||
{
|
||||
SimWorker worker;
|
||||
int numcycles = 20;
|
||||
bool start_set = false, stop_set = false, at_set = false;
|
||||
|
||||
log_header(design, "Executing SIM pass (simulate the circuit).\n");
|
||||
|
||||
|
@ -822,8 +1175,15 @@ struct SimPass : public Pass {
|
|||
worker.vcdfile.open(vcd_filename.c_str());
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-fst" && argidx+1 < args.size()) {
|
||||
std::string fst_filename = args[++argidx];
|
||||
rewrite_filename(fst_filename);
|
||||
worker.fstfile = (struct fstContext *)fstWriterCreate(fst_filename.c_str(),1);
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-n" && argidx+1 < args.size()) {
|
||||
numcycles = atoi(args[++argidx].c_str());
|
||||
worker.cycles_set = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-rstlen" && argidx+1 < args.size()) {
|
||||
|
@ -866,9 +1226,55 @@ struct SimPass : public Pass {
|
|||
worker.zinit = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-r" && argidx+1 < args.size()) {
|
||||
std::string sim_filename = args[++argidx];
|
||||
rewrite_filename(sim_filename);
|
||||
worker.sim_filename = sim_filename;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-scope" && argidx+1 < args.size()) {
|
||||
worker.scope = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-start" && argidx+1 < args.size()) {
|
||||
worker.start_time = stringToTime(args[++argidx]);
|
||||
start_set = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-stop" && argidx+1 < args.size()) {
|
||||
worker.stop_time = stringToTime(args[++argidx]);
|
||||
stop_set = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-at" && argidx+1 < args.size()) {
|
||||
worker.start_time = stringToTime(args[++argidx]);
|
||||
worker.stop_time = worker.start_time;
|
||||
at_set = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-sim") {
|
||||
worker.sim_mode = SimulationMode::sim;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-sim-cmp") {
|
||||
worker.sim_mode = SimulationMode::cmp;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-sim-gold") {
|
||||
worker.sim_mode = SimulationMode::gold;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-sim-gate") {
|
||||
worker.sim_mode = SimulationMode::gate;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
if (at_set && (start_set || stop_set || worker.cycles_set))
|
||||
log_error("'at' option can only be defined separate of 'start','stop' and 'n'\n");
|
||||
if (stop_set && worker.cycles_set)
|
||||
log_error("'stop' and 'n' can only be used exclusively'\n");
|
||||
|
||||
Module *top_mod = nullptr;
|
||||
|
||||
|
@ -884,7 +1290,10 @@ struct SimPass : public Pass {
|
|||
top_mod = mods.front();
|
||||
}
|
||||
|
||||
worker.run(top_mod, numcycles);
|
||||
if (worker.sim_filename.empty())
|
||||
worker.run(top_mod, numcycles);
|
||||
else
|
||||
worker.run_cosim(top_mod, numcycles);
|
||||
}
|
||||
} SimPass;
|
||||
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
*.log
|
||||
run-test.mk
|
||||
*.vcd
|
||||
*.fst
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
module alu(
|
||||
input clk,
|
||||
input [7:0] A,
|
||||
input [7:0] B,
|
||||
input [3:0] operation,
|
||||
output reg [7:0] result,
|
||||
output reg CF,
|
||||
output reg ZF,
|
||||
output reg SF
|
||||
);
|
||||
|
||||
localparam ALU_OP_ADD /* verilator public_flat */ = 4'b0000;
|
||||
localparam ALU_OP_SUB /* verilator public_flat */ = 4'b0001;
|
||||
localparam ALU_OP_ADC /* verilator public_flat */ = 4'b0010;
|
||||
localparam ALU_OP_SBC /* verilator public_flat */ = 4'b0011;
|
||||
|
||||
localparam ALU_OP_AND /* verilator public_flat */ = 4'b0100;
|
||||
localparam ALU_OP_OR /* verilator public_flat */ = 4'b0101;
|
||||
localparam ALU_OP_NOT /* verilator public_flat */ = 4'b0110;
|
||||
localparam ALU_OP_XOR /* verilator public_flat */ = 4'b0111;
|
||||
|
||||
localparam ALU_OP_SHL /* verilator public_flat */ = 4'b1000;
|
||||
localparam ALU_OP_SHR /* verilator public_flat */ = 4'b1001;
|
||||
localparam ALU_OP_SAL /* verilator public_flat */ = 4'b1010;
|
||||
localparam ALU_OP_SAR /* verilator public_flat */ = 4'b1011;
|
||||
|
||||
localparam ALU_OP_ROL /* verilator public_flat */ = 4'b1100;
|
||||
localparam ALU_OP_ROR /* verilator public_flat */ = 4'b1101;
|
||||
localparam ALU_OP_RCL /* verilator public_flat */ = 4'b1110;
|
||||
localparam ALU_OP_RCR /* verilator public_flat */ = 4'b1111;
|
||||
|
||||
reg [8:0] tmp;
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
case (operation)
|
||||
ALU_OP_ADD :
|
||||
tmp = A + B;
|
||||
ALU_OP_SUB :
|
||||
tmp = A - B;
|
||||
ALU_OP_ADC :
|
||||
tmp = A + B + { 7'b0000000, CF };
|
||||
ALU_OP_SBC :
|
||||
tmp = A - B - { 7'b0000000, CF };
|
||||
ALU_OP_AND :
|
||||
tmp = {1'b0, A & B };
|
||||
ALU_OP_OR :
|
||||
tmp = {1'b0, A | B };
|
||||
ALU_OP_NOT :
|
||||
tmp = {1'b0, ~B };
|
||||
ALU_OP_XOR :
|
||||
tmp = {1'b0, A ^ B};
|
||||
ALU_OP_SHL :
|
||||
tmp = { A[7], A[6:0], 1'b0};
|
||||
ALU_OP_SHR :
|
||||
tmp = { A[0], 1'b0, A[7:1]};
|
||||
ALU_OP_SAL :
|
||||
// Same as SHL
|
||||
tmp = { A[7], A[6:0], 1'b0};
|
||||
ALU_OP_SAR :
|
||||
tmp = { A[0], A[7], A[7:1]};
|
||||
ALU_OP_ROL :
|
||||
tmp = { A[7], A[6:0], A[7]};
|
||||
ALU_OP_ROR :
|
||||
tmp = { A[0], A[0], A[7:1]};
|
||||
ALU_OP_RCL :
|
||||
tmp = { A[7], A[6:0], CF};
|
||||
ALU_OP_RCR :
|
||||
tmp = { A[0], CF, A[7:1]};
|
||||
endcase
|
||||
|
||||
CF <= tmp[8];
|
||||
ZF <= tmp[7:0] == 0;
|
||||
SF <= tmp[7];
|
||||
|
||||
result <= tmp[7:0];
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
read_verilog grom_computer.v grom_cpu.v alu.v ram_memory.v;
|
||||
prep -top grom_computer;
|
||||
sim -clock clk -reset reset -fst grom.fst -vcd grom.vcd -n 80
|
||||
|
||||
sim -clock clk -r grom.fst -scope grom_computer -start 25ns -stop 100ns -sim-cmp
|
||||
|
||||
sim -clock clk -r grom.fst -scope grom_computer -stop 100ns -sim-gold
|
||||
|
||||
sim -clock clk -r grom.fst -scope grom_computer -n 10 -sim-gate
|
|
@ -0,0 +1,31 @@
|
|||
module grom_computer
|
||||
(input clk, // Main Clock
|
||||
input reset, // reset
|
||||
output hlt,
|
||||
output reg[7:0] display_out
|
||||
);
|
||||
|
||||
wire [11:0] addr;
|
||||
wire [7:0] memory_out;
|
||||
wire [7:0] memory_in;
|
||||
wire mem_enable;
|
||||
wire we;
|
||||
wire ioreq;
|
||||
|
||||
grom_cpu cpu(.clk(clk),.reset(reset),.addr(addr),.data_in(memory_out),.data_out(memory_in),.we(we),.ioreq(ioreq),.hlt(hlt));
|
||||
|
||||
assign mem_enable = we & ~ioreq;
|
||||
|
||||
ram_memory memory(.clk(clk),.addr(addr),.data_in(memory_in),.we(mem_enable),.data_out(memory_out));
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(ioreq==1 && we==1)
|
||||
begin
|
||||
display_out <= memory_in;
|
||||
`ifdef DISASSEMBLY
|
||||
$display("Display output : %h", memory_in);
|
||||
`endif
|
||||
end
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,747 @@
|
|||
module grom_cpu(
|
||||
input clk,
|
||||
input reset,
|
||||
output reg [11:0] addr,
|
||||
input [7:0] data_in,
|
||||
output reg [7:0] data_out,
|
||||
output reg we,
|
||||
output reg ioreq,
|
||||
output reg hlt
|
||||
);
|
||||
|
||||
reg[11:0] PC /* verilator public_flat */; // Program counter
|
||||
reg[7:0] IR /* verilator public_flat */; // Instruction register
|
||||
reg[7:0] VALUE /* verilator public_flat */; // Temp reg for storing 2nd operand
|
||||
reg[3:0] CS /* verilator public_flat */; // Code segment regiser
|
||||
reg[3:0] DS /* verilator public_flat */; // Data segment regiser
|
||||
reg[11:0] SP /* verilator public_flat */; // Stack pointer regiser
|
||||
reg[7:0] R[0:3] /* verilator public_flat */; // General purpose registers
|
||||
reg[11:0] FUTURE_PC /* verilator public_flat */; // PC to jump to
|
||||
|
||||
localparam STATE_RESET /*verilator public_flat*/ = 5'b00000;
|
||||
localparam STATE_FETCH_PREP /*verilator public_flat*/ = 5'b00001;
|
||||
localparam STATE_FETCH_WAIT /*verilator public_flat*/ = 5'b00010;
|
||||
localparam STATE_FETCH /*verilator public_flat*/ = 5'b00011;
|
||||
localparam STATE_EXECUTE /*verilator public_flat*/ = 5'b00100;
|
||||
localparam STATE_FETCH_VALUE_PREP /*verilator public_flat*/ = 5'b00101;
|
||||
localparam STATE_FETCH_VALUE /*verilator public_flat*/ = 5'b00110;
|
||||
localparam STATE_EXECUTE_DBL /*verilator public_flat*/ = 5'b00111;
|
||||
localparam STATE_LOAD_VALUE /*verilator public_flat*/ = 5'b01000;
|
||||
localparam STATE_LOAD_VALUE_WAIT /*verilator public_flat*/ = 5'b01001;
|
||||
localparam STATE_ALU_RESULT_WAIT /*verilator public_flat*/ = 5'b01010;
|
||||
localparam STATE_ALU_RESULT /*verilator public_flat*/ = 5'b01011;
|
||||
localparam STATE_PUSH_PC_LOW /*verilator public_flat*/ = 5'b01100;
|
||||
localparam STATE_JUMP /*verilator public_flat*/ = 5'b01101;
|
||||
localparam STATE_RET_VALUE_WAIT /*verilator public_flat*/ = 5'b01110;
|
||||
localparam STATE_RET_VALUE /*verilator public_flat*/ = 5'b01111;
|
||||
localparam STATE_RET_VALUE_WAIT2 /*verilator public_flat*/ = 5'b10000;
|
||||
localparam STATE_RET_VALUE2 /*verilator public_flat*/ = 5'b10001;
|
||||
|
||||
reg [4:0] state /* verilator public_flat */ = STATE_RESET;
|
||||
|
||||
reg [7:0] alu_a /* verilator public_flat */;
|
||||
reg [7:0] alu_b /* verilator public_flat */;
|
||||
reg [3:0] alu_op /* verilator public_flat */;
|
||||
|
||||
reg [1:0] RESULT_REG /* verilator public_flat */;
|
||||
|
||||
wire [7:0] alu_res /* verilator public_flat */;
|
||||
wire alu_CF /* verilator public_flat */;
|
||||
wire alu_ZF /* verilator public_flat */;
|
||||
wire alu_SF /* verilator public_flat */;
|
||||
reg jump;
|
||||
|
||||
alu alu(.clk(clk),.A(alu_a),.B(alu_b),.operation(alu_op),.result(alu_res),.CF(alu_CF),.ZF(alu_ZF),.SF(alu_SF));
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (reset)
|
||||
begin
|
||||
state <= STATE_RESET;
|
||||
hlt <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
case (state)
|
||||
STATE_RESET :
|
||||
begin
|
||||
PC <= 12'h000;
|
||||
state <= STATE_FETCH_PREP;
|
||||
CS <= 4'h0;
|
||||
DS <= 4'h0;
|
||||
R[0] <= 8'h00;
|
||||
R[1] <= 8'h00;
|
||||
R[2] <= 8'h00;
|
||||
R[3] <= 8'h00;
|
||||
SP <= 12'hfff;
|
||||
end
|
||||
|
||||
STATE_FETCH_PREP :
|
||||
begin
|
||||
addr <= PC;
|
||||
we <= 0;
|
||||
ioreq <= 0;
|
||||
|
||||
state <= STATE_FETCH_WAIT;
|
||||
end
|
||||
|
||||
STATE_FETCH_WAIT :
|
||||
begin
|
||||
// Sync with memory due to CLK
|
||||
state <= (hlt) ? STATE_FETCH_PREP : STATE_FETCH;
|
||||
end
|
||||
|
||||
STATE_FETCH :
|
||||
begin
|
||||
IR <= data_in;
|
||||
PC <= PC + 1;
|
||||
|
||||
state <= STATE_EXECUTE;
|
||||
end
|
||||
STATE_EXECUTE :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display(" PC %h R0 %h R1 %h R2 %h R3 %h CS %h DS %h SP %h ALU [%d %d %d]", PC, R[0], R[1], R[2], R[3], CS, DS, SP, alu_CF,alu_SF,alu_ZF);
|
||||
`endif
|
||||
if (IR[7])
|
||||
begin
|
||||
addr <= PC;
|
||||
state <= STATE_FETCH_VALUE_PREP;
|
||||
PC <= PC + 1;
|
||||
end
|
||||
else
|
||||
begin
|
||||
case(IR[6:4])
|
||||
3'b000 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("MOV R%d,R%d",IR[3:2],IR[1:0]);
|
||||
`endif
|
||||
R[IR[3:2]] <= R[IR[1:0]];
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
3'b001 :
|
||||
begin
|
||||
alu_a <= R[0]; // first input R0
|
||||
alu_b <= R[IR[1:0]];
|
||||
RESULT_REG <= 0; // result in R0
|
||||
alu_op <= { 2'b00, IR[3:2] };
|
||||
|
||||
state <= STATE_ALU_RESULT_WAIT;
|
||||
|
||||
`ifdef DISASSEMBLY
|
||||
case(IR[3:2])
|
||||
2'b00 : begin
|
||||
$display("ADD R%d",IR[1:0]);
|
||||
end
|
||||
2'b01 : begin
|
||||
$display("SUB R%d",IR[1:0]);
|
||||
end
|
||||
2'b10 : begin
|
||||
$display("ADC R%d",IR[1:0]);
|
||||
end
|
||||
2'b11 : begin
|
||||
$display("SBC R%d",IR[1:0]);
|
||||
end
|
||||
endcase
|
||||
`endif
|
||||
end
|
||||
3'b010 :
|
||||
begin
|
||||
alu_a <= R[0]; // first input R0
|
||||
alu_b <= R[IR[1:0]];
|
||||
RESULT_REG <= 0; // result in R0
|
||||
alu_op <= { 2'b01, IR[3:2] };
|
||||
state <= STATE_ALU_RESULT_WAIT;
|
||||
`ifdef DISASSEMBLY
|
||||
case(IR[3:2])
|
||||
2'b00 : begin
|
||||
$display("AND R%d",IR[1:0]);
|
||||
end
|
||||
2'b01 : begin
|
||||
$display("OR R%d",IR[1:0]);
|
||||
end
|
||||
2'b10 : begin
|
||||
$display("NOT R%d",IR[1:0]);
|
||||
end
|
||||
2'b11 : begin
|
||||
$display("XOR R%d",IR[1:0]);
|
||||
end
|
||||
endcase
|
||||
`endif
|
||||
end
|
||||
3'b011 :
|
||||
begin
|
||||
RESULT_REG <= IR[1:0]; // result in REG
|
||||
// CMP and TEST are not storing result
|
||||
state <= IR[3] ? STATE_FETCH_PREP : STATE_ALU_RESULT_WAIT;
|
||||
// CMP and TEST are having first input R0, for INC and DEC is REG
|
||||
alu_a <= IR[3] ? R[0] : R[IR[1:0]];
|
||||
// CMP and TEST are having second input REG, for INC and DEC is 1
|
||||
alu_b <= IR[3] ? R[IR[1:0]] : 8'b00000001;
|
||||
|
||||
case(IR[3:2])
|
||||
2'b00 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("INC R%d",IR[1:0]);
|
||||
`endif
|
||||
alu_op <= 4'b0001; // ALU_OP_ADD
|
||||
end
|
||||
2'b01 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("DEC R%d",IR[1:0]);
|
||||
`endif
|
||||
alu_op <= 4'b0001; // ALU_OP_SUB
|
||||
end
|
||||
2'b10 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("CMP R%d",IR[1:0]);
|
||||
`endif
|
||||
alu_op <= 4'b0001; // ALU_OP_SUB
|
||||
end
|
||||
2'b11 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("TST R%d",IR[1:0]);
|
||||
`endif
|
||||
alu_op <= 4'b0100; // ALU_OP_AND
|
||||
end
|
||||
endcase
|
||||
end
|
||||
3'b100 :
|
||||
begin
|
||||
if (IR[3]==0)
|
||||
begin
|
||||
alu_a <= R[0]; // first input R0
|
||||
// no 2nd input
|
||||
RESULT_REG <= 0; // result in R0
|
||||
alu_op <= { 1'b1, IR[2:0] };
|
||||
`ifdef DISASSEMBLY
|
||||
case(IR[2:0])
|
||||
3'b000 : begin
|
||||
$display("SHL");
|
||||
end
|
||||
3'b001 : begin
|
||||
$display("SHR");
|
||||
end
|
||||
3'b010 : begin
|
||||
$display("SAL");
|
||||
end
|
||||
3'b011 : begin
|
||||
$display("SAR");
|
||||
end
|
||||
3'b100 : begin
|
||||
$display("ROL");
|
||||
end
|
||||
3'b101 : begin
|
||||
$display("ROR");
|
||||
end
|
||||
3'b110 : begin
|
||||
$display("RCL");
|
||||
end
|
||||
3'b111 : begin
|
||||
$display("RCR");
|
||||
end
|
||||
endcase
|
||||
`endif
|
||||
state <= STATE_ALU_RESULT_WAIT;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if (IR[2]==0)
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("PUSH R%d",IR[1:0]);
|
||||
`endif
|
||||
addr <= SP;
|
||||
we <= 1;
|
||||
ioreq <= 0;
|
||||
data_out <= R[IR[1:0]];
|
||||
SP <= SP - 1;
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
else
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("POP R%d",IR[1:0]);
|
||||
`endif
|
||||
addr <= SP + 1;
|
||||
we <= 0;
|
||||
ioreq <= 0;
|
||||
RESULT_REG <= IR[1:0];
|
||||
SP <= SP + 1;
|
||||
state <= STATE_LOAD_VALUE_WAIT;
|
||||
end
|
||||
end
|
||||
end
|
||||
3'b101 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("LOAD R%d,[R%d]", IR[3:2], IR[1:0]);
|
||||
`endif
|
||||
addr <= { DS, R[IR[1:0]] };
|
||||
we <= 0;
|
||||
ioreq <= 0;
|
||||
RESULT_REG <= IR[3:2];
|
||||
|
||||
state <= STATE_LOAD_VALUE_WAIT;
|
||||
end
|
||||
3'b110 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("STORE [R%d],R%d", IR[3:2], IR[1:0]);
|
||||
`endif
|
||||
addr <= { DS, R[IR[3:2]] };
|
||||
we <= 1;
|
||||
ioreq <= 0;
|
||||
data_out <= R[IR[1:0]];
|
||||
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
3'b111 :
|
||||
begin
|
||||
// Special instuctions
|
||||
case(IR[3:2])
|
||||
2'b00 : begin
|
||||
CS <= R[IR[1:0]][3:0];
|
||||
state <= STATE_FETCH_PREP;
|
||||
`ifdef DISASSEMBLY
|
||||
$display("MOV CS,R%d",IR[1:0]);
|
||||
`endif
|
||||
end
|
||||
2'b01 : begin
|
||||
DS <= R[IR[1:0]][3:0];
|
||||
state <= STATE_FETCH_PREP;
|
||||
`ifdef DISASSEMBLY
|
||||
$display("MOV DS,R%d",IR[1:0]);
|
||||
`endif
|
||||
end
|
||||
2'b10 : begin
|
||||
case(IR[1:0])
|
||||
2'b00 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("PUSH CS");
|
||||
`endif
|
||||
addr <= SP;
|
||||
we <= 1;
|
||||
ioreq <= 0;
|
||||
data_out <= { 4'b0000, CS};
|
||||
SP <= SP - 1;
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
2'b01 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("PUSH DS");
|
||||
`endif
|
||||
addr <= SP;
|
||||
we <= 1;
|
||||
ioreq <= 0;
|
||||
data_out <= { 4'b0000, DS};
|
||||
SP <= SP - 1;
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
2'b10 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("Unused opcode");
|
||||
`endif
|
||||
end
|
||||
2'b11 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("Unused opcode");
|
||||
`endif
|
||||
end
|
||||
endcase
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
2'b11 : begin
|
||||
case(IR[1:0])
|
||||
2'b00 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("Unused opcode");
|
||||
`endif
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
2'b01 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("Unused opcode");
|
||||
`endif
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
2'b10 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("RET");
|
||||
`endif
|
||||
addr <= SP + 1;
|
||||
we <= 0;
|
||||
ioreq <= 0;
|
||||
SP <= SP + 1;
|
||||
state <= STATE_RET_VALUE_WAIT;
|
||||
end
|
||||
2'b11 : begin
|
||||
hlt <= 1;
|
||||
`ifdef DISASSEMBLY
|
||||
$display("HALT");
|
||||
`endif
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
endcase
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
STATE_FETCH_VALUE_PREP :
|
||||
begin
|
||||
// Sync with memory due to CLK
|
||||
state <= STATE_FETCH_VALUE;
|
||||
end
|
||||
STATE_FETCH_VALUE :
|
||||
begin
|
||||
VALUE <= data_in;
|
||||
state <= STATE_EXECUTE_DBL;
|
||||
end
|
||||
STATE_EXECUTE_DBL :
|
||||
begin
|
||||
case(IR[6:4])
|
||||
3'b000 :
|
||||
begin
|
||||
if (IR[3]==0)
|
||||
begin
|
||||
case(IR[2:0])
|
||||
3'b000 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("JMP %h ",{ CS, VALUE[7:0] });
|
||||
`endif
|
||||
jump = 1;
|
||||
end
|
||||
3'b001 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("JC %h ",{CS, VALUE[7:0] });
|
||||
`endif
|
||||
jump = (alu_CF==1);
|
||||
end
|
||||
3'b010 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("JNC %h ",{CS, VALUE[7:0] });
|
||||
`endif
|
||||
jump = (alu_CF==0);
|
||||
end
|
||||
3'b011 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("JM %h ",{CS, VALUE[7:0] });
|
||||
`endif
|
||||
jump = (alu_SF==1);
|
||||
end
|
||||
3'b100 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("JP %h ",{CS, VALUE[7:0] });
|
||||
`endif
|
||||
jump = (alu_SF==0);
|
||||
end
|
||||
3'b101 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("JZ %h ",{CS, VALUE[7:0] });
|
||||
`endif
|
||||
jump = (alu_ZF==1);
|
||||
end
|
||||
3'b110 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("JNZ %h ",{CS, VALUE[7:0] });
|
||||
`endif
|
||||
jump = (alu_ZF==0);
|
||||
end
|
||||
3'b111 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("Unused opcode %h",IR);
|
||||
`endif
|
||||
jump = 0;
|
||||
end
|
||||
endcase
|
||||
|
||||
if (jump)
|
||||
begin
|
||||
PC <= { CS, VALUE[7:0] };
|
||||
addr <= { CS, VALUE[7:0] };
|
||||
we <= 0;
|
||||
ioreq <= 0;
|
||||
end
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
else
|
||||
begin
|
||||
case(IR[2:0])
|
||||
3'b000 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("JR %h ", PC + {VALUE[7],VALUE[7],VALUE[7],VALUE[7],VALUE[7:0]} );
|
||||
`endif
|
||||
jump = 1;
|
||||
end
|
||||
3'b001 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("JRC %h ",{CS, VALUE[7:0] });
|
||||
`endif
|
||||
jump = (alu_CF==1);
|
||||
end
|
||||
3'b010 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("JRNC %h ",{CS, VALUE[7:0] });
|
||||
`endif
|
||||
jump = (alu_CF==0);
|
||||
end
|
||||
3'b011 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("JRM %h ",{CS, VALUE[7:0] });
|
||||
`endif
|
||||
jump = (alu_SF==1);
|
||||
end
|
||||
3'b100 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("JRP %h ",{CS, VALUE[7:0] });
|
||||
`endif
|
||||
jump = (alu_SF==0);
|
||||
end
|
||||
3'b101 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("JRZ %h ",{CS, VALUE[7:0] });
|
||||
`endif
|
||||
jump = (alu_ZF==1);
|
||||
end
|
||||
3'b110 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("JRNZ %h ",{CS, VALUE[7:0] });
|
||||
`endif
|
||||
jump = (alu_ZF==0);
|
||||
end
|
||||
3'b111 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("Unused opcode %h",IR);
|
||||
`endif
|
||||
jump = 0;
|
||||
end
|
||||
endcase
|
||||
if (jump)
|
||||
begin
|
||||
PC <= PC + {VALUE[7],VALUE[7],VALUE[7],VALUE[7],VALUE[7:0]};
|
||||
addr <= PC + {VALUE[7],VALUE[7],VALUE[7],VALUE[7],VALUE[7:0]};
|
||||
we <= 0;
|
||||
ioreq <= 0;
|
||||
end
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
end
|
||||
3'b001 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("JUMP %h ",{ IR[3:0], VALUE[7:0] });
|
||||
`endif
|
||||
PC <= { IR[3:0], VALUE[7:0] };
|
||||
addr <= { IR[3:0], VALUE[7:0] };
|
||||
we <= 0;
|
||||
ioreq <= 0;
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
3'b010 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("CALL %h ",{ IR[3:0], VALUE[7:0] });
|
||||
`endif
|
||||
FUTURE_PC <= { IR[3:0], VALUE[7:0] };
|
||||
addr <= SP;
|
||||
we <= 1;
|
||||
ioreq <= 0;
|
||||
data_out <= { 4'b0000, PC[11:8]};
|
||||
SP <= SP - 1;
|
||||
state <= STATE_PUSH_PC_LOW;
|
||||
end
|
||||
3'b011 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("MOV SP,%h ",{ IR[3:0], VALUE[7:0] });
|
||||
`endif
|
||||
SP <= { IR[3:0], VALUE[7:0] };
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
3'b100 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("IN R%d,[0x%h]",IR[1:0], VALUE);
|
||||
`endif
|
||||
ioreq <= 1;
|
||||
we <= 0;
|
||||
addr <= { 4'b0000, VALUE };
|
||||
RESULT_REG <= IR[1:0];
|
||||
state <= STATE_LOAD_VALUE_WAIT;
|
||||
end
|
||||
3'b101 :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("OUT [0x%h],R%d",VALUE,IR[1:0]);
|
||||
`endif
|
||||
ioreq <= 1;
|
||||
we <= 1;
|
||||
addr <= { 4'b0000, VALUE };
|
||||
data_out <= R[IR[1:0]];
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
3'b110 :
|
||||
begin
|
||||
// Special instuctions
|
||||
case(IR[1:0])
|
||||
2'b00 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("MOV CS,0x%h",VALUE);
|
||||
`endif
|
||||
CS <= VALUE[3:0];
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
2'b01 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("MOV DS,0x%h",VALUE);
|
||||
`endif
|
||||
DS <= VALUE[3:0];
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
2'b10 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("Unused opcode %h",IR);
|
||||
`endif
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
2'b11 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("Unused opcode %h",IR);
|
||||
`endif
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
3'b111 :
|
||||
begin
|
||||
case(IR[3:2])
|
||||
2'b00 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("MOV R%d,0x%h",IR[1:0],VALUE);
|
||||
`endif
|
||||
R[IR[1:0]] <= VALUE;
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
2'b01 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("LOAD R%d,[0x%h]",IR[1:0], {DS, VALUE});
|
||||
`endif
|
||||
addr <= { DS, VALUE };
|
||||
we <= 0;
|
||||
ioreq <= 0;
|
||||
RESULT_REG <= IR[1:0];
|
||||
|
||||
state <= STATE_LOAD_VALUE_WAIT;
|
||||
end
|
||||
2'b10 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("STORE [0x%h],R%d", {DS, VALUE}, IR[1:0]);
|
||||
`endif
|
||||
addr <= { DS, VALUE };
|
||||
we <= 1;
|
||||
ioreq <= 0;
|
||||
data_out <= R[IR[1:0]];
|
||||
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
2'b11 : begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("Unused opcode %h",IR);
|
||||
`endif
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
endcase
|
||||
end
|
||||
STATE_LOAD_VALUE_WAIT :
|
||||
begin
|
||||
// Sync with memory due to CLK
|
||||
state <= STATE_LOAD_VALUE;
|
||||
end
|
||||
STATE_LOAD_VALUE :
|
||||
begin
|
||||
R[RESULT_REG] <= data_in;
|
||||
we <= 0;
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
STATE_ALU_RESULT_WAIT :
|
||||
begin
|
||||
state <= STATE_ALU_RESULT;
|
||||
end
|
||||
STATE_ALU_RESULT :
|
||||
begin
|
||||
R[RESULT_REG] <= alu_res;
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
STATE_PUSH_PC_LOW :
|
||||
begin
|
||||
addr <= SP;
|
||||
we <= 1;
|
||||
ioreq <= 0;
|
||||
data_out <= PC[7:0];
|
||||
SP <= SP - 1;
|
||||
state <= STATE_JUMP;
|
||||
end
|
||||
STATE_JUMP :
|
||||
begin
|
||||
`ifdef DISASSEMBLY
|
||||
$display("Jumping to %h",FUTURE_PC);
|
||||
`endif
|
||||
PC <= FUTURE_PC;
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
STATE_RET_VALUE_WAIT :
|
||||
begin
|
||||
// Sync with memory due to CLK
|
||||
state <= STATE_RET_VALUE;
|
||||
end
|
||||
STATE_RET_VALUE :
|
||||
begin
|
||||
FUTURE_PC <= { 4'b0000, data_in };
|
||||
we <= 0;
|
||||
state <= STATE_RET_VALUE_WAIT2;
|
||||
|
||||
addr <= SP + 1;
|
||||
we <= 0;
|
||||
ioreq <= 0;
|
||||
SP <= SP + 1;
|
||||
end
|
||||
STATE_RET_VALUE_WAIT2 :
|
||||
begin
|
||||
// Sync with memory due to CLK
|
||||
state <= STATE_RET_VALUE2;
|
||||
end
|
||||
STATE_RET_VALUE2 :
|
||||
begin
|
||||
FUTURE_PC <= FUTURE_PC | ({ 4'b0000, data_in } << 8);
|
||||
we <= 0;
|
||||
state <= STATE_JUMP;
|
||||
end
|
||||
default :
|
||||
begin
|
||||
state <= STATE_FETCH_PREP;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,37 @@
|
|||
module ram_memory(
|
||||
input clk,
|
||||
input [11:0] addr,
|
||||
input [7:0] data_in,
|
||||
input we,
|
||||
output reg [7:0] data_out
|
||||
);
|
||||
|
||||
reg [7:0] store[0:4095] /* verilator public_flat */;
|
||||
|
||||
initial
|
||||
begin
|
||||
store[0] <= 8'b11100001; // MOV DS,2
|
||||
store[1] <= 8'b00000010; //
|
||||
store[2] <= 8'b01010100; // LOAD R1,[R0]
|
||||
store[3] <= 8'b00110001; // INC R1
|
||||
store[4] <= 8'b00110001; // INC R1
|
||||
store[5] <= 8'b01100001; // STORE [R0],R1
|
||||
store[6] <= 8'b11010001; // OUT [0],R1
|
||||
store[7] <= 8'b00000000; //
|
||||
store[8] <= 8'b00110001; // INC R1
|
||||
store[9] <= 8'b10100001; // CALL 0x100
|
||||
store[10] <= 8'b00000000; //
|
||||
store[11] <= 8'b01111111; // HLT
|
||||
|
||||
|
||||
store[256] <= 8'b11010001; // OUT [0],R1
|
||||
store[257] <= 8'b00000000; //
|
||||
store[258] <= 8'b01111110; // RET
|
||||
end
|
||||
|
||||
always @(posedge clk)
|
||||
if (we)
|
||||
store[addr] <= data_in;
|
||||
else
|
||||
data_out <= store[addr];
|
||||
endmodule
|
|
@ -0,0 +1,48 @@
|
|||
# Create stimulus file
|
||||
read_verilog <<EOT
|
||||
module top (clk, reset, cnt);
|
||||
|
||||
input clk;
|
||||
input reset;
|
||||
output [7:0] cnt;
|
||||
|
||||
reg [7:0] cnt;
|
||||
|
||||
endmodule
|
||||
EOT
|
||||
prep -top top;
|
||||
sim -clock clk -reset reset -fst stimulus.fst -n 10
|
||||
design -reset
|
||||
|
||||
# Counter implementation
|
||||
read_verilog <<EOT
|
||||
module top (clk, reset, cnt);
|
||||
|
||||
input clk;
|
||||
input reset;
|
||||
output [7:0] cnt;
|
||||
|
||||
reg [7:0] cnt;
|
||||
|
||||
always @(posedge clk)
|
||||
if (!reset)
|
||||
cnt = cnt + 1;
|
||||
else
|
||||
cnt = 0;
|
||||
|
||||
endmodule
|
||||
EOT
|
||||
prep -top top;
|
||||
|
||||
# Simulate with stimulus
|
||||
sim -clock clk -scope top -r stimulus.fst
|
||||
|
||||
# Stimulus does not have counter values
|
||||
# x in FST can match any value in simulation
|
||||
sim -clock clk -scope top -r stimulus.fst -sim-gate
|
||||
|
||||
# Stimulus does not have counter values
|
||||
# x in simulation can match any value in FST
|
||||
# so we expect error
|
||||
logger -expect error "Signal difference" 1
|
||||
sim -clock clk -scope top -r stimulus.fst -sim-gold
|
Loading…
Reference in New Issue