aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/test-linux.yml9
-rw-r--r--.github/workflows/test-macos.yml29
-rw-r--r--CHANGELOG17
-rw-r--r--Makefile8
-rw-r--r--backends/btor/btor.cc4
-rw-r--r--backends/json/json.cc19
-rw-r--r--backends/smt2/smt2.cc6
-rw-r--r--backends/smt2/smtio.py10
-rw-r--r--frontends/json/jsonparse.cc34
-rw-r--r--frontends/verific/verific.cc5
-rw-r--r--kernel/fstdata.cc20
-rw-r--r--kernel/fstdata.h1
-rw-r--r--manual/command-reference-manual.tex97
-rw-r--r--passes/sat/sim.cc743
-rw-r--r--techlibs/gowin/cells_sim.v10
-rw-r--r--techlibs/intel_alm/Makefile.inc1
-rw-r--r--techlibs/intel_alm/common/bram_m10k.txt4
-rw-r--r--techlibs/intel_alm/common/bram_m10k_map.v16
-rw-r--r--techlibs/intel_alm/common/mem_sim.v2
-rw-r--r--techlibs/intel_alm/common/quartus_rename.v9
-rw-r--r--techlibs/intel_alm/synth_intel_alm.cc3
-rw-r--r--tests/arch/intel_alm/blockram.ys3
-rw-r--r--tests/sat/grom_cpu.v2
-rw-r--r--tests/sat/ram_memory.v2
-rw-r--r--tests/various/.gitignore1
-rw-r--r--tests/various/json_escape_chars.ys14
26 files changed, 823 insertions, 246 deletions
diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml
index 9aa952e45..e27ea37d2 100644
--- a/.github/workflows/test-linux.yml
+++ b/.github/workflows/test-linux.yml
@@ -11,9 +11,7 @@ jobs:
- { id: ubuntu-20.04, name: focal }
compiler:
- 'clang-12'
- - 'clang-11'
- 'gcc-11'
- - 'gcc-10'
cpp_std:
- 'c++11'
- 'c++14'
@@ -21,6 +19,12 @@ jobs:
- 'c++20'
include:
# Limit the older compilers to C++11 mode
+ - os: { id: ubuntu-20.04, name: focal }
+ compiler: 'clang-11'
+ cpp_std: 'c++11'
+ - os: { id: ubuntu-20.04, name: focal }
+ compiler: 'gcc-10'
+ cpp_std: 'c++11'
- os: { id: ubuntu-18.04, name: bionic }
compiler: 'clang-3.9'
cpp_std: 'c++11'
@@ -120,6 +124,7 @@ jobs:
make -j${{ env.procs }} CCXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC
- name: Run tests
+ if: (matrix.cpp_std == 'c++11') && (matrix.compiler == 'gcc-11')
shell: bash
run: |
make -j${{ env.procs }} test CXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC
diff --git a/.github/workflows/test-macos.yml b/.github/workflows/test-macos.yml
index 09ab382bf..b14ce8633 100644
--- a/.github/workflows/test-macos.yml
+++ b/.github/workflows/test-macos.yml
@@ -67,6 +67,7 @@ jobs:
make -j${{ env.procs }} CXXSTD=${{ matrix.cpp_std }} CC=cc CXX=cc LD=cc
- name: Run tests
+ if: matrix.cpp_std == 'c++11'
shell: bash
run: |
make -j${{ env.procs }} test CXXSTD=${{ matrix.cpp_std }} CC=cc CXX=cc LD=cc
@@ -119,36 +120,8 @@ jobs:
- name: Checkout Yosys
uses: actions/checkout@v2
- - name: Get iverilog
- shell: bash
- run: |
- git clone https://github.com/steveicarus/iverilog.git
-
- - name: Cache iverilog
- id: cache-iverilog-homebrew
- uses: actions/cache@v2
- with:
- path: .local/
- key: ${{ matrix.os.id }}-homebrew-${{ hashFiles('iverilog/.git/refs/heads/master') }}
-
- - name: Build iverilog
- if: steps.cache-iverilog.outputs.cache-hit != 'true'
- shell: bash
- run: |
- mkdir -p $GITHUB_WORKSPACE/.local
- cd iverilog
- autoconf
- CC=gcc CXX=g++ ./configure --prefix=$GITHUB_WORKSPACE/.local
- make -j${{ env.procs }}
- make install
-
- name: Build yosys
shell: bash
run: |
make config-gcc
make -j${{ env.procs }} CXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC
-
- - name: Run tests
- shell: bash
- run: |
- make -j${{ env.procs }} test CXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC
diff --git a/CHANGELOG b/CHANGELOG
index dcb88d6fe..d6d2c4990 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,9 +2,17 @@
List of major changes and improvements between releases
=======================================================
-Yosys 0.14 .. Yosys 0.14-dev
+Yosys 0.15 .. Yosys 0.15-dev
--------------------------
+Yosys 0.14 .. Yosys 0.15
+--------------------------
+
+ * Various
+ - clk2fflogic: nice names for autogenerated signals
+ - simulation include support for all flip-flop types.
+ - Added AIGER witness file co-simulation.
+
* Verilog
- Fixed evaluation of constant functions with variables or arguments with
reversed dimensions
@@ -14,6 +22,13 @@ Yosys 0.14 .. Yosys 0.14-dev
* SystemVerilog
- Added support for accessing whole sub-structures in expressions
+
+ * New commands and options
+ - Added glift command, used to create gate-level information flow tracking
+ (GLIFT) models by the "constructive mapping" approach
+
+ * Verific support
+ - Ability to override default parser mode for verific -f command.
Yosys 0.13 .. Yosys 0.14
--------------------------
diff --git a/Makefile b/Makefile
index 85fe9183d..db975fc14 100644
--- a/Makefile
+++ b/Makefile
@@ -129,12 +129,12 @@ LDFLAGS += -rdynamic
LDLIBS += -lrt
endif
-YOSYS_VER := 0.14+51
+YOSYS_VER := 0.15+31
GIT_REV := $(shell git -C $(YOSYS_SRC) rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
OBJS = kernel/version_$(GIT_REV).o
bumpversion:
- sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline a4522d6.. | wc -l`/;" Makefile
+ sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 07a4368.. | wc -l`/;" Makefile
# set 'ABCREV = default' to use abc/ as it is
#
@@ -142,7 +142,7 @@ bumpversion:
# is just a symlink to your actual ABC working directory, as 'make mrproper'
# will remove the 'abc' directory and you do not want to accidentally
# delete your work on ABC..
-ABCREV = b4790a6
+ABCREV = d7ecb23
ABCPULL = 1
ABCURL ?= https://github.com/YosysHQ/abc
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 VERBOSE=$(Q)
@@ -916,7 +916,7 @@ clean:
rm -rf tests/simple/*.out tests/simple/*.log
rm -rf tests/memories/*.out tests/memories/*.log tests/memories/*.dmp
rm -rf tests/sat/*.log tests/techmap/*.log tests/various/*.log
- rm -rf tests/bram/temp tests/fsm/temp tests/realmath/temp tests/share/temp tests/smv/temp
+ rm -rf tests/bram/temp tests/fsm/temp tests/realmath/temp tests/share/temp tests/smv/temp tests/various/temp
rm -rf vloghtb/Makefile vloghtb/refdat vloghtb/rtl vloghtb/scripts vloghtb/spec vloghtb/check_yosys vloghtb/vloghammer_tb.tar.bz2 vloghtb/temp vloghtb/log_test_*
rm -f tests/svinterfaces/*.log_stdout tests/svinterfaces/*.log_stderr tests/svinterfaces/dut_result.txt tests/svinterfaces/reference_result.txt tests/svinterfaces/a.out tests/svinterfaces/*_syn.v tests/svinterfaces/*.diff
rm -f tests/tools/cmp_tbdata
diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc
index d62cc4c3d..73e88c049 100644
--- a/backends/btor/btor.cc
+++ b/backends/btor/btor.cc
@@ -678,7 +678,7 @@ struct BtorWorker
int sid = get_bv_sid(GetSize(sig_y));
int nid = next_nid++;
- btorf("%d state %d\n", nid, sid);
+ btorf("%d state %d%s\n", nid, sid, getinfo(cell).c_str());
if (cell->type == ID($anyconst)) {
int nid2 = next_nid++;
@@ -699,7 +699,7 @@ struct BtorWorker
int one_nid = get_sig_nid(State::S1);
int zero_nid = get_sig_nid(State::S0);
initstate_nid = next_nid++;
- btorf("%d state %d\n", initstate_nid, sid);
+ btorf("%d state %d%s\n", initstate_nid, sid, getinfo(cell).c_str());
btorf("%d init %d %d %d\n", next_nid++, sid, initstate_nid, one_nid);
btorf("%d next %d %d %d\n", next_nid++, sid, initstate_nid, zero_nid);
}
diff --git a/backends/json/json.cc b/backends/json/json.cc
index 4aa8046d6..270d762ee 100644
--- a/backends/json/json.cc
+++ b/backends/json/json.cc
@@ -52,8 +52,23 @@ struct JsonWriter
string newstr = "\"";
for (char c : str) {
if (c == '\\')
+ newstr += "\\\\";
+ else if (c == '"')
+ newstr += "\\\"";
+ else if (c == '\b')
+ newstr += "\\b";
+ else if (c == '\f')
+ newstr += "\\f";
+ else if (c == '\n')
+ newstr += "\\n";
+ else if (c == '\r')
+ newstr += "\\r";
+ else if (c == '\t')
+ newstr += "\\t";
+ else if (c < 0x20)
+ newstr += stringf("\\u%04X", c);
+ else
newstr += c;
- newstr += c;
}
return newstr + "\"";
}
@@ -379,6 +394,7 @@ struct JsonBackend : public Backend {
log(" \"bits\": <bit_vector>\n");
log(" \"offset\": <the lowest bit index in use, if non-0>\n");
log(" \"upto\": <1 if the port bit indexing is MSB-first>\n");
+ log(" \"signed\": <1 if the port is signed>\n");
log(" }\n");
log("\n");
log("The \"offset\" and \"upto\" fields are skipped if their value would be 0.");
@@ -428,6 +444,7 @@ struct JsonBackend : public Backend {
log(" \"bits\": <bit_vector>\n");
log(" \"offset\": <the lowest bit index in use, if non-0>\n");
log(" \"upto\": <1 if the port bit indexing is MSB-first>\n");
+ log(" \"signed\": <1 if the port is signed>\n");
log(" }\n");
log("\n");
log("The \"hide_name\" fields are set to 1 when the name of this cell or net is\n");
diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc
index a928419a1..9bf0de03e 100644
--- a/backends/smt2/smt2.cc
+++ b/backends/smt2/smt2.cc
@@ -985,8 +985,10 @@ struct Smt2Worker
string name_a = get_bool(cell->getPort(ID::A));
string name_en = get_bool(cell->getPort(ID::EN));
- string infostr = (cell->name[0] == '$' && cell->attributes.count(ID::src)) ? cell->attributes.at(ID::src).decode_string() : get_id(cell);
- decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id, infostr.c_str()));
+ if (cell->name[0] == '$' && cell->attributes.count(ID::src))
+ decls.push_back(stringf("; yosys-smt2-%s %d %s %s\n", cell->type.c_str() + 1, id, get_id(cell), cell->attributes.at(ID::src).decode_string().c_str()));
+ else
+ decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id, get_id(cell)));
if (cell->type == ID($cover))
decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (and %s %s)) ; %s\n",
diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py
index d73a875ba..3d458e6cf 100644
--- a/backends/smt2/smtio.py
+++ b/backends/smt2/smtio.py
@@ -536,10 +536,16 @@ class SmtIo:
self.modinfo[self.curmod].clocks[fields[2]] = "event"
if fields[1] == "yosys-smt2-assert":
- self.modinfo[self.curmod].asserts["%s_a %s" % (self.curmod, fields[2])] = fields[3]
+ if len(fields) > 4:
+ self.modinfo[self.curmod].asserts["%s_a %s" % (self.curmod, fields[2])] = f'{fields[4]} ({fields[3]})'
+ else:
+ self.modinfo[self.curmod].asserts["%s_a %s" % (self.curmod, fields[2])] = fields[3]
if fields[1] == "yosys-smt2-cover":
- self.modinfo[self.curmod].covers["%s_c %s" % (self.curmod, fields[2])] = fields[3]
+ if len(fields) > 4:
+ self.modinfo[self.curmod].covers["%s_c %s" % (self.curmod, fields[2])] = f'{fields[4]} ({fields[3]})'
+ else:
+ self.modinfo[self.curmod].covers["%s_c %s" % (self.curmod, fields[2])] = fields[3]
if fields[1] == "yosys-smt2-maximize":
self.modinfo[self.curmod].maximize.add(fields[2])
diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc
index 50c25abda..1aab81015 100644
--- a/frontends/json/jsonparse.cc
+++ b/frontends/json/jsonparse.cc
@@ -60,10 +60,38 @@ struct JsonNode
break;
if (ch == '\\') {
- int ch = f.get();
+ ch = f.get();
- if (ch == EOF)
- log_error("Unexpected EOF in JSON string.\n");
+ switch (ch) {
+ case EOF: log_error("Unexpected EOF in JSON string.\n"); break;
+ case '"':
+ case '/':
+ case '\\': break;
+ case 'b': ch = '\b'; break;
+ case 'f': ch = '\f'; break;
+ case 'n': ch = '\n'; break;
+ case 'r': ch = '\r'; break;
+ case 't': ch = '\t'; break;
+ case 'u':
+ int val = 0;
+ for (int i = 0; i < 4; i++) {
+ ch = f.get();
+ val <<= 4;
+ if (ch >= '0' && '9' >= ch) {
+ val += ch - '0';
+ } else if (ch >= 'A' && 'F' >= ch) {
+ val += 10 + ch - 'A';
+ } else if (ch >= 'a' && 'f' >= ch) {
+ val += 10 + ch - 'a';
+ } else
+ log_error("Unexpected non-digit character in \\uXXXX sequence: %c.\n", ch);
+ }
+ if (val < 128)
+ ch = val;
+ else
+ log_error("Unsupported \\uXXXX sequence in JSON string: %04X.\n", val);
+ break;
+ }
}
data_string += ch;
diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc
index 17dbed067..8c2f7f0da 100644
--- a/frontends/verific/verific.cc
+++ b/frontends/verific/verific.cc
@@ -169,7 +169,10 @@ void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &att
FOREACH_ATTRIBUTE(obj, mi, attr) {
if (attr->Key()[0] == ' ' || attr->Value() == nullptr)
continue;
- attributes[RTLIL::escape_id(attr->Key())] = RTLIL::Const(std::string(attr->Value()));
+ std::string val = std::string(attr->Value());
+ if (val.size()>1 && val[0]=='\"' && val.back()=='\"')
+ val = val.substr(1,val.size()-2);
+ attributes[RTLIL::escape_id(attr->Key())] = RTLIL::Const(val);
}
if (nl) {
diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc
index 2e1000178..587678ce3 100644
--- a/kernel/fstdata.cc
+++ b/kernel/fstdata.cc
@@ -22,8 +22,25 @@
USING_YOSYS_NAMESPACE
+static std::string file_base_name(std::string const & path)
+{
+ return path.substr(path.find_last_of("/\\") + 1);
+}
+
FstData::FstData(std::string filename) : ctx(nullptr)
{
+ #if !defined(YOSYS_DISABLE_SPAWN)
+ std::string filename_trim = file_base_name(filename);
+ if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".vcd") == 0) {
+ filename_trim.erase(filename_trim.size()-4);
+ tmp_file = stringf("/tmp/converted_%s.fst", filename_trim.c_str());
+ std::string cmd = stringf("vcd2fst %s %s", filename.c_str(), tmp_file.c_str());
+ log("Exec: %s\n", cmd.c_str());
+ if (run_command(cmd) != 0)
+ log_cmd_error("Shell command failed!\n");
+ filename = tmp_file;
+ }
+ #endif
const std::vector<std::string> g_units = { "s", "ms", "us", "ns", "ps", "fs", "as", "zs" };
ctx = (fstReaderContext *)fstReaderOpen(filename.c_str());
if (!ctx)
@@ -53,6 +70,8 @@ FstData::~FstData()
{
if (ctx)
fstReaderClose(ctx);
+ if (!tmp_file.empty())
+ remove(tmp_file.c_str());
}
uint64_t FstData::getStartTime() { return fstReaderGetStartTime(ctx); }
@@ -184,6 +203,7 @@ void FstData::reconstructAllAtTimes(std::vector<fstHandle> &signal, uint64_t sta
fstReaderSetUnlimitedTimeRange(ctx);
fstReaderSetFacProcessMaskAll(ctx);
fstReaderIterBlocks2(ctx, reconstruct_clb_attimes, reconstruct_clb_varlen_attimes, this, nullptr);
+ past_data = last_data;
callback(last_time);
if (last_time!=end_time)
callback(end_time);
diff --git a/kernel/fstdata.h b/kernel/fstdata.h
index 707d1b64e..4a355fd77 100644
--- a/kernel/fstdata.h
+++ b/kernel/fstdata.h
@@ -74,6 +74,7 @@ private:
CallbackFunction callback;
std::vector<fstHandle> clk_signals;
bool all_samples;
+ std::string tmp_file;
};
YOSYS_NAMESPACE_END
diff --git a/manual/command-reference-manual.tex b/manual/command-reference-manual.tex
index 1aa0facb5..e3055c0bc 100644
--- a/manual/command-reference-manual.tex
+++ b/manual/command-reference-manual.tex
@@ -2222,6 +2222,89 @@ one-hot encoding and binary encoding is supported.
.map <old_bitpattern> <new_bitpattern>
\end{lstlisting}
+\section{glift -- create GLIFT models and optimization problems}
+\label{cmd:glift}
+\begin{lstlisting}[numbers=left,frame=single]
+ glift <command> [options] [selection]
+
+Augments the current or specified module with gate-level information flow tracking
+(GLIFT) logic using the "constructive mapping" approach. Also can set up QBF-SAT
+optimization problems in order to optimize GLIFT models or trade off precision and
+complexity.
+
+
+Commands:
+
+ -create-precise-model
+ Replaces the current or specified module with one that has corresponding "taint"
+ inputs, outputs, and internal nets along with precise taint tracking logic.
+ For example, precise taint tracking logic for an AND gate is:
+
+ y_t = a & b_t | b & a_t | a_t & b_t
+
+
+ -create-imprecise-model
+ Replaces the current or specified module with one that has corresponding "taint"
+ inputs, outputs, and internal nets along with imprecise "All OR" taint tracking
+ logic:
+
+ y_t = a_t | b_t
+
+
+ -create-instrumented-model
+ Replaces the current or specified module with one that has corresponding "taint"
+ inputs, outputs, and internal nets along with 4 varying-precision versions of taint
+ tracking logic. Which version of taint tracking logic is used for a given gate is
+ determined by a MUX selected by an $anyconst cell. By default, unless the
+ `-no-cost-model` option is provided, an additional wire named `__glift_weight` with
+ the `keep` and `minimize` attributes is added to the module along with pmuxes and
+ adders to calculate a rough estimate of the number of logic gates in the GLIFT model
+ given an assignment for the $anyconst cells. The four versions of taint tracking logic
+ for an AND gate are:
+ y_t = a & b_t | b & a_t | a_t & b_t (like `-create-precise-model`)
+ y_t = a_t | a & b_t
+ y_t = b_t | b & a_t
+ y_t = a_t | b_t (like `-create-imprecise-model`)
+
+
+Options:
+
+ -taint-constants
+ Constant values in the design are labeled as tainted.
+ (default: label constants as un-tainted)
+
+ -keep-outputs
+ Do not remove module outputs. Taint tracking outputs will appear in the module ports
+ alongside the orignal outputs.
+ (default: original module outputs are removed)
+
+ -simple-cost-model
+ Do not model logic area. Instead model the number of non-zero assignments to $anyconsts.
+ Taint tracking logic versions vary in their size, but all reduced-precision versions are
+ significantly smaller than the fully-precise version. A non-zero $anyconst assignment means
+ that reduced-precision taint tracking logic was chosen for some gate.
+ Only applicable in combination with `-create-instrumented-model`.
+ (default: use a complex model and give that wire the "keep" and "minimize" attributes)
+
+ -no-cost-model
+ Do not model taint tracking logic area and do not create a `__glift_weight` wire.
+ Only applicable in combination with `-create-instrumented-model`.
+ (default: model area and give that wire the "keep" and "minimize" attributes)
+
+ -instrument-more
+ Allow choice from more versions of (even simpler) taint tracking logic. A total
+ of 8 versions of taint tracking logic will be added per gate, including the 4
+ versions from `-create-instrumented-model` and these additional versions:
+
+ y_t = a_t
+ y_t = b_t
+ y_t = 1
+ y_t = 0
+
+ Only applicable in combination with `-create-instrumented-model`.
+ (default: do not add more versions of taint tracking logic.
+\end{lstlisting}
+
\section{greenpak4\_dffinv -- merge greenpak4 inverters and DFF/latches}
\label{cmd:greenpak4_dffinv}
\begin{lstlisting}[numbers=left,frame=single]
@@ -4834,6 +4917,13 @@ This command simulates the circuit using the given top-level module.
-fst <filename>
write the simulation results to the given FST file
+ -aiw <filename>
+ write the simulation results to an AIGER witness file
+ (requires a *.aim file via -map)
+
+ -x
+ ignore constant x outputs in simulation file.
+
-clock <portname>
name of top-level clock input
@@ -4867,6 +4957,9 @@ This command simulates the circuit using the given top-level module.
-r
read simulation results file (file formats supported: FST)
+ -map <filename>
+ read file with port and latch symbols, needed for AIGER witness input
+
-scope
scope of simulation top model
@@ -7551,9 +7644,11 @@ Like -sv, but define FORMAL instead of SYNTHESIS.
Load the specified VHDL files into Verific.
- verific {-f|-F} <command-file>
+ verific {-f|-F} [-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|-sv|-formal] <command-file>
Load and execute the specified command file.
+Override verilog parsing mode can be set.
+The macros YOSYS, SYNTHESIS/FORMAL, and VERIFIC are defined implicitly.
Command file parser supports following commands:
+define - defines macro
diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc
index ff3a30889..5b69dd3bf 100644
--- a/passes/sat/sim.cc
+++ b/passes/sat/sim.cc
@@ -65,9 +65,19 @@ static double stringToTime(std::string str)
return value * pow(10.0, g_units.at(endptr));
}
+struct SimWorker;
+struct OutputWriter
+{
+ OutputWriter(SimWorker *w) { worker = w;};
+ virtual ~OutputWriter() {};
+ virtual void write(std::map<int, bool> &use_signal) = 0;
+ SimWorker *worker;
+};
+
struct SimShared
{
bool debug = false;
+ bool verbose = true;
bool hide_internal = true;
bool writeback = false;
bool zinit = false;
@@ -77,6 +87,10 @@ struct SimShared
double stop_time = -1;
SimulationMode sim_mode = SimulationMode::sim;
bool cycles_set = false;
+ std::vector<std::unique_ptr<OutputWriter>> outputfiles;
+ std::vector<std::pair<int,std::map<int,Const>>> output_data;
+ bool ignore_x = false;
+ bool date = false;
};
void zinit(State &v)
@@ -140,8 +154,7 @@ struct SimInstance
std::vector<Mem> memories;
- dict<Wire*, pair<int, Const>> vcd_database;
- dict<Wire*, pair<fstHandle, Const>> fst_database;
+ dict<Wire*, pair<int, Const>> signal_database;
dict<Wire*, fstHandle> fst_handles;
SimInstance(SimShared *shared, std::string scope, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) :
@@ -170,7 +183,7 @@ 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());
+ log_warning("Unable to find wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(wire->name)).c_str());
fst_handles[wire] = id;
}
@@ -315,6 +328,16 @@ struct SimInstance
return did_something;
}
+ void set_memory_state(IdString memid, Const addr, Const data)
+ {
+ auto &state = mem_database[memid];
+
+ int offset = (addr.as_int() - state.mem->start_offset) * state.mem->width;
+ for (int i = 0; i < GetSize(data); i++)
+ if (0 <= i+offset && i+offset < GetSize(data))
+ state.data.bits[i+offset] = data.bits[i];
+ }
+
void update_cell(Cell *cell)
{
if (ff_database.count(cell))
@@ -685,100 +708,53 @@ struct SimInstance
it.second->writeback(wbmods);
}
- void write_vcd_header(std::ofstream &f, int &id)
+ void register_signals(int &id)
{
- f << stringf("$scope module %s $end\n", log_id(name()));
-
for (auto wire : module->wires())
{
if (shared->hide_internal && wire->name[0] == '$')
continue;
- f << stringf("$var wire %d n%d %s%s $end\n", GetSize(wire), id, wire->name[0] == '$' ? "\\" : "", log_id(wire));
- vcd_database[wire] = make_pair(id++, Const());
+ signal_database[wire] = make_pair(id, Const());
+ id++;
}
for (auto child : children)
- child.second->write_vcd_header(f, id);
-
- f << stringf("$upscope $end\n");
+ child.second->register_signals(id);
}
- void write_vcd_step(std::ofstream &f)
+ void write_output_header(std::function<void(IdString)> enter_scope, std::function<void()> exit_scope, std::function<void(Wire*, int)> register_signal)
{
- for (auto &it : vcd_database)
- {
- Wire *wire = it.first;
- Const value = get_state(wire);
- int id = it.second.first;
+ enter_scope(name());
- if (it.second.second == value)
- continue;
-
- it.second.second = value;
-
- f << "b";
- for (int i = GetSize(value)-1; i >= 0; i--) {
- switch (value[i]) {
- case State::S0: f << "0"; break;
- case State::S1: f << "1"; break;
- case State::Sx: f << "x"; break;
- default: f << "z";
- }
- }
-
- f << stringf(" n%d\n", id);
- }
-
- 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())
+ for (auto signal : signal_database)
{
- 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());
+ register_signal(signal.first, signal.second.first);
}
for (auto child : children)
- child.second->write_fst_header(f);
+ child.second->write_output_header(enter_scope, exit_scope, register_signal);
- fstWriterSetUpscope(f);
+ exit_scope();
}
- void write_fst_step(struct fstContext *f)
+ void register_output_step_values(std::map<int,Const> *data)
{
- for (auto &it : fst_database)
+ for (auto &it : signal_database)
{
Wire *wire = it.first;
Const value = get_state(wire);
- fstHandle id = it.second.first;
+ int 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());
+ data->emplace(id, value);
}
for (auto child : children)
- child.second->write_fst_step(f);
+ child.second->register_output_step_values(data);
}
void setInitState()
@@ -790,7 +766,7 @@ struct SimInstance
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());
+ log_warning("Unable to find wire %s in input file.\n", (scope + "." + RTLIL::unescape_id(name)).c_str());
if (id!=0) {
Const fst_val = Const::from_string(shared->fst->valueOf(id));
set_state(qsig, fst_val);
@@ -801,14 +777,15 @@ struct SimInstance
child.second->setInitState();
}
- void setState(std::vector<std::pair<SigBit,bool>> bits, std::string values)
+ void setState(dict<int, std::pair<SigBit,bool>> bits, std::string values)
{
- for(size_t i=0;i<bits.size();i++) {
- switch(values.at(i)) {
- case '0' : set_state(bits.at(i).first,bits.at(i).second ? State::S1 : State::S0); break;
- case '1' : set_state(bits.at(i).first,bits.at(i).second ? State::S0 : State::S1); break;
- default:
- set_state(bits.at(i).first,State::Sx); break;
+ for(auto bit : bits) {
+ if (bit.first >= GetSize(values))
+ log_error("Too few input data bits in file.\n");
+ switch(values.at(bit.first)) {
+ case '0': set_state(bit.second.first, bit.second.second ? State::S1 : State::S0); break;
+ case '1': set_state(bit.second.first, bit.second.second ? State::S0 : State::S1); break;
+ default: set_state(bit.second.first, State::Sx); break;
}
}
}
@@ -856,8 +833,6 @@ struct SimInstance
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;
@@ -866,75 +841,41 @@ struct SimWorker : SimShared
~SimWorker()
{
+ outputfiles.clear();
delete top;
}
- void write_vcd_header()
+ void register_signals()
{
- vcdfile << stringf("$version %s $end\n", yosys_version_str);
-
- std::time_t t = std::time(nullptr);
- char mbstr[255];
- if (std::strftime(mbstr, sizeof(mbstr), "%c", std::localtime(&t))) {
- vcdfile << stringf("$date ") << mbstr << stringf(" $end\n");
- }
-
- if (!timescale.empty())
- vcdfile << stringf("$timescale %s $end\n", timescale.c_str());
-
int id = 1;
- top->write_vcd_header(vcdfile, id);
-
- vcdfile << stringf("$enddefinitions $end\n");
- }
-
- void write_vcd_step(int t)
- {
- 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();
+ top->register_signals(id);
}
- void write_output_step(int t)
+ void register_output_step(int t)
{
- if (vcdfile.is_open())
- write_vcd_step(t);
- if (fstfile)
- write_fst_step(t);
+ std::map<int,Const> data;
+ top->register_output_step_values(&data);
+ output_data.emplace_back(t, data);
}
- void write_output_end()
+ void write_output_files()
{
- if (fstfile)
- fstWriterClose(fstfile);
+ std::map<int, bool> use_signal;
+ bool first = ignore_x;
+ for(auto& d : output_data)
+ {
+ if (first) {
+ for (auto &data : d.second)
+ use_signal[data.first] = !data.second.is_fully_undef();
+ first = false;
+ } else {
+ for (auto &data : d.second)
+ use_signal[data.first] = true;
+ }
+ if (!ignore_x) break;
+ }
+ for(auto& writer : outputfiles)
+ writer->write(use_signal);
}
void update()
@@ -976,10 +917,11 @@ struct SimWorker : SimShared
{
log_assert(top == nullptr);
top = new SimInstance(this, scope, topmod);
+ register_signals();
if (debug)
log("\n===== 0 =====\n");
- else
+ else if (verbose)
log("Simulating cycle 0.\n");
set_inports(reset, State::S1);
@@ -990,24 +932,23 @@ struct SimWorker : SimShared
update();
- write_output_header();
- write_output_step(0);
+ register_output_step(0);
for (int cycle = 0; cycle < numcycles; cycle++)
{
if (debug)
log("\n===== %d =====\n", 10*cycle + 5);
- else
+ else if (verbose)
log("Simulating cycle %d.\n", (cycle*2)+1);
set_inports(clock, State::S0);
set_inports(clockn, State::S1);
update();
- write_output_step(10*cycle + 5);
+ register_output_step(10*cycle + 5);
if (debug)
log("\n===== %d =====\n", 10*cycle + 10);
- else
+ else if (verbose)
log("Simulating cycle %d.\n", (cycle*2)+2);
set_inports(clock, State::S1);
@@ -1019,12 +960,12 @@ struct SimWorker : SimShared
}
update();
- write_output_step(10*cycle + 10);
+ register_output_step(10*cycle + 10);
}
- write_output_step(10*numcycles + 2);
+ register_output_step(10*numcycles + 2);
- write_output_end();
+ write_output_files();
if (writeback) {
pool<Module*> wbmods;
@@ -1032,7 +973,7 @@ struct SimWorker : SimShared
}
}
- void run_cosim(Module *topmod, int numcycles)
+ void run_cosim_fst(Module *topmod, int numcycles)
{
log_assert(top == nullptr);
fst = new FstData(sim_filename);
@@ -1041,6 +982,7 @@ struct SimWorker : SimShared
log_error("Scope must be defined for co-simulation.\n");
top = new SimInstance(this, scope, topmod);
+ register_signals();
std::vector<fstHandle> fst_clock;
@@ -1123,19 +1065,21 @@ struct SimWorker : SimShared
try {
fst->reconstructAllAtTimes(fst_clock, startCount, stopCount, [&](uint64_t time) {
- log("Co-simulating %s %d [%lu%s].\n", (all_samples ? "sample" : "cycle"), cycle, (unsigned long)time, fst->getTimescaleString());
+ if (verbose)
+ log("Co-simulating %s %d [%lu%s].\n", (all_samples ? "sample" : "cycle"), cycle, (unsigned long)time, fst->getTimescaleString());
+ bool did_something = false;
for(auto &item : inputs) {
std::string v = fst->valueOf(item.second);
- top->set_state(item.first, Const::from_string(v));
+ did_something |= top->set_state(item.first, Const::from_string(v));
}
if (initial) {
top->setInitState();
- write_output_header();
initial = false;
}
- update();
- write_output_step(5*cycle);
+ if (did_something)
+ update();
+ register_output_step(time);
bool status = top->checkSignals();
if (status)
@@ -1151,23 +1095,27 @@ struct SimWorker : SimShared
} catch(fst_end_of_data_exception) {
// end of data detected
}
- write_output_step(5*(cycle-1)+2);
- write_output_end();
+
+ write_output_files();
if (writeback) {
pool<Module*> wbmods;
top->writeback(wbmods);
}
+ delete fst;
}
- void run_cosim_witness(Module *topmod)
+ void run_cosim_aiger_witness(Module *topmod)
{
log_assert(top == nullptr);
+ if ((clock.size()+clockn.size())==0)
+ log_error("Clock signal must be specified.\n");
std::ifstream mf(map_filename);
std::string type, symbol;
int variable, index;
- std::vector<std::pair<SigBit,bool>> inputs;
- std::vector<std::pair<SigBit,bool>> latches;
+ dict<int, std::pair<SigBit,bool>> inputs, inits, latches;
+ if (mf.fail())
+ log_cmd_error("Not able to read AIGER witness map file.\n");
while (mf >> type >> variable >> index >> symbol) {
RTLIL::IdString escaped_s = RTLIL::escape_id(symbol);
Wire *w = topmod->wire(escaped_s);
@@ -1176,11 +1124,13 @@ struct SimWorker : SimShared
if (index < w->start_offset || index > w->start_offset + w->width)
log_error("Index %d for wire %s is out of range\n", index, log_signal(w));
if (type == "input") {
- inputs.emplace_back(SigBit(w,index),false);
+ inputs[variable] = {SigBit(w,index), false};
+ } else if (type == "init") {
+ inits[variable] = {SigBit(w,index), false};
} else if (type == "latch") {
- latches.emplace_back(SigBit(w,index),false);
+ latches[variable] = {SigBit(w,index), false};
} else if (type == "invlatch") {
- latches.emplace_back(SigBit(w,index),true);
+ latches[variable] = {SigBit(w,index), true};
}
}
@@ -1189,46 +1139,415 @@ struct SimWorker : SimShared
if (f.fail() || GetSize(sim_filename) == 0)
log_error("Can not open file `%s`\n", sim_filename.c_str());
- bool init = true;
+ int state = 0;
+ std::string status;
int cycle = 0;
top = new SimInstance(this, scope, topmod);
+ register_signals();
+
while (!f.eof())
{
std::string line;
std::getline(f, line);
if (line.size()==0 || line[0]=='#') continue;
- if (init) {
- if (line.size()!=latches.size())
- log_error("Wrong number of initialization bits in file.\n");
- write_output_header();
- top->setState(latches, line);
- init = false;
- } else {
- log("Simulating cycle %d.\n", cycle);
- if (line.size()!=inputs.size())
- log_error("Wrong number of input data bits in file.\n");
- top->setState(inputs, line);
- if (cycle) {
+ if (line[0]=='.') break;
+ if (state==0 && line.size()!=1) {
+ // old format detected, latch data
+ state = 2;
+ }
+ if (state==1 && line[0]!='b' && line[0]!='c') {
+ // was old format but with 1 bit latch
+ top->setState(latches, status);
+ state = 3;
+ }
+
+ switch(state)
+ {
+ case 0:
+ status = line;
+ state = 1;
+ break;
+ case 1:
+ state = 2;
+ break;
+ case 2:
+ top->setState(latches, line);
+ state = 3;
+ break;
+ default:
+ if (verbose)
+ log("Simulating cycle %d.\n", cycle);
+ top->setState(inputs, line);
+ if (cycle) {
+ set_inports(clock, State::S1);
+ set_inports(clockn, State::S0);
+ } else {
+ top->setState(inits, line);
+ set_inports(clock, State::S0);
+ set_inports(clockn, State::S1);
+ }
+ update();
+ register_output_step(10*cycle);
+ if (cycle) {
+ set_inports(clock, State::S0);
+ set_inports(clockn, State::S1);
+ update();
+ register_output_step(10*cycle + 5);
+ }
+ cycle++;
+ break;
+ }
+ }
+ register_output_step(10*cycle);
+ write_output_files();
+ }
+
+ std::vector<std::string> split(std::string text, const char *delim)
+ {
+ std::vector<std::string> list;
+ char *p = strdup(text.c_str());
+ char *t = strtok(p, delim);
+ while (t != NULL) {
+ list.push_back(t);
+ t = strtok(NULL, delim);
+ }
+ free(p);
+ return list;
+ }
+
+ std::string signal_name(std::string const & name)
+ {
+ size_t pos = name.find_first_of("@");
+ if (pos==std::string::npos) {
+ pos = name.find_first_of("#");
+ if (pos==std::string::npos)
+ log_error("Line does not contain proper signal name `%s`\n", name.c_str());
+ }
+ return name.substr(0, pos);
+ }
+
+ void run_cosim_btor2_witness(Module *topmod)
+ {
+ log_assert(top == nullptr);
+ if ((clock.size()+clockn.size())==0)
+ log_error("Clock signal must be specified.\n");
+ std::ifstream f;
+ f.open(sim_filename.c_str());
+ if (f.fail() || GetSize(sim_filename) == 0)
+ log_error("Can not open file `%s`\n", sim_filename.c_str());
+
+ int state = 0;
+ int cycle = 0;
+ top = new SimInstance(this, scope, topmod);
+ register_signals();
+ int prev_cycle = 0;
+ int curr_cycle = 0;
+ std::vector<std::string> parts;
+ size_t len = 0;
+ while (!f.eof())
+ {
+ std::string line;
+ std::getline(f, line);
+ if (line.size()==0) continue;
+
+ if (line[0]=='#' || line[0]=='@' || line[0]=='.') {
+ if (line[0]!='.')
+ curr_cycle = atoi(line.c_str()+1);
+ else
+ curr_cycle = -1; // force detect change
+
+ if (curr_cycle != prev_cycle) {
+ if (verbose)
+ log("Simulating cycle %d.\n", cycle);
set_inports(clock, State::S1);
set_inports(clockn, State::S0);
- } else {
- set_inports(clock, State::S0);
- set_inports(clockn, State::S1);
- }
- update();
- write_output_step(10*cycle);
- if (cycle) {
+ update();
+ register_output_step(10*cycle+0);
set_inports(clock, State::S0);
set_inports(clockn, State::S1);
update();
- write_output_step(10*cycle + 5);
+ register_output_step(10*cycle+5);
+ cycle++;
+ prev_cycle = curr_cycle;
}
- cycle++;
+ if (line[0]=='.') break;
+ continue;
+ }
+
+ switch(state)
+ {
+ case 0:
+ if (line=="sat")
+ state = 1;
+ break;
+ case 1:
+ if (line[0]=='b' || line[0]=='j')
+ state = 2;
+ else
+ log_error("Line does not contain property.\n");
+ break;
+ default: // set state or inputs
+ parts = split(line, " ");
+ len = parts.size();
+ if (len<3 || len>4)
+ log_error("Invalid set state line content.\n");
+
+ RTLIL::IdString escaped_s = RTLIL::escape_id(signal_name(parts[len-1]));
+ if (len==3) {
+ Wire *w = topmod->wire(escaped_s);
+ if (!w) {
+ Cell *c = topmod->cell(escaped_s);
+ if (!c)
+ log_warning("Wire/cell %s not present in module %s\n",log_id(escaped_s),log_id(topmod));
+ else if (c->type.in(ID($anyconst), ID($anyseq))) {
+ SigSpec sig_y= c->getPort(ID::Y);
+ if ((int)parts[1].size() != GetSize(sig_y))
+ log_error("Size of wire %s is different than provided data.\n", log_signal(sig_y));
+ top->set_state(sig_y, Const::from_string(parts[1]));
+ }
+ } else {
+ if ((int)parts[1].size() != w->width)
+ log_error("Size of wire %s is different than provided data.\n", log_signal(w));
+ top->set_state(w, Const::from_string(parts[1]));
+ }
+ } else {
+ Cell *c = topmod->cell(escaped_s);
+ if (!c)
+ log_error("Cell %s not present in module %s\n",log_id(escaped_s),log_id(topmod));
+ if (!c->is_mem_cell())
+ log_error("Cell %s is not memory cell in module %s\n",log_id(escaped_s),log_id(topmod));
+
+ Const addr = Const::from_string(parts[1].substr(1,parts[1].size()-2));
+ Const data = Const::from_string(parts[2]);
+ top->set_memory_state(c->parameters.at(ID::MEMID).decode_string(), addr, data);
+ }
+ break;
+ }
+ }
+ register_output_step(10*cycle);
+ write_output_files();
+ }
+};
+
+struct VCDWriter : public OutputWriter
+{
+ VCDWriter(SimWorker *worker, std::string filename) : OutputWriter(worker) {
+ vcdfile.open(filename.c_str());
+ }
+
+ void write(std::map<int, bool> &use_signal) override
+ {
+ if (!vcdfile.is_open()) return;
+ vcdfile << stringf("$version %s $end\n", worker->date ? yosys_version_str : "Yosys");
+
+ if (worker->date) {
+ std::time_t t = std::time(nullptr);
+ char mbstr[255];
+ if (std::strftime(mbstr, sizeof(mbstr), "%c", std::localtime(&t))) {
+ vcdfile << stringf("$date ") << mbstr << stringf(" $end\n");
+ }
+ }
+
+ if (!worker->timescale.empty())
+ vcdfile << stringf("$timescale %s $end\n", worker->timescale.c_str());
+
+ worker->top->write_output_header(
+ [this](IdString name) { vcdfile << stringf("$scope module %s $end\n", log_id(name)); },
+ [this]() { vcdfile << stringf("$upscope $end\n");},
+ [this,use_signal](Wire *wire, int id) { if (use_signal.at(id)) vcdfile << stringf("$var wire %d n%d %s%s $end\n", GetSize(wire), id, wire->name[0] == '$' ? "\\" : "", log_id(wire)); }
+ );
+
+ vcdfile << stringf("$enddefinitions $end\n");
+
+ for(auto& d : worker->output_data)
+ {
+ vcdfile << stringf("#%d\n", d.first);
+ for (auto &data : d.second)
+ {
+ if (!use_signal.at(data.first)) continue;
+ Const value = data.second;
+ vcdfile << "b";
+ for (int i = GetSize(value)-1; i >= 0; i--) {
+ switch (value[i]) {
+ case State::S0: vcdfile << "0"; break;
+ case State::S1: vcdfile << "1"; break;
+ case State::Sx: vcdfile << "x"; break;
+ default: vcdfile << "z";
+ }
+ }
+ vcdfile << stringf(" n%d\n", data.first);
+ }
+ }
+ }
+
+ std::ofstream vcdfile;
+};
+
+struct FSTWriter : public OutputWriter
+{
+ FSTWriter(SimWorker *worker, std::string filename) : OutputWriter(worker) {
+ fstfile = (struct fstContext *)fstWriterCreate(filename.c_str(),1);
+ }
+
+ virtual ~FSTWriter()
+ {
+ fstWriterClose(fstfile);
+ }
+
+ void write(std::map<int, bool> &use_signal) override
+ {
+ if (!fstfile) return;
+ std::time_t t = std::time(nullptr);
+ fstWriterSetVersion(fstfile, worker->date ? yosys_version_str : "Yosys");
+ if (worker->date)
+ fstWriterSetDate(fstfile, asctime(std::localtime(&t)));
+ else
+ fstWriterSetDate(fstfile, "");
+ if (!worker->timescale.empty())
+ fstWriterSetTimescaleFromString(fstfile, worker->timescale.c_str());
+
+ fstWriterSetPackType(fstfile, FST_WR_PT_FASTLZ);
+ fstWriterSetRepackOnClose(fstfile, 1);
+
+ worker->top->write_output_header(
+ [this](IdString name) { fstWriterSetScope(fstfile, FST_ST_VCD_MODULE, stringf("%s",log_id(name)).c_str(), nullptr); },
+ [this]() { fstWriterSetUpscope(fstfile); },
+ [this,use_signal](Wire *wire, int id) {
+ if (!use_signal.at(id)) return;
+ fstHandle fst_id = fstWriterCreateVar(fstfile, FST_VT_VCD_WIRE, FST_VD_IMPLICIT, GetSize(wire),
+ stringf("%s%s", wire->name[0] == '$' ? "\\" : "", log_id(wire)).c_str(), 0);
+
+ mapping.emplace(id, fst_id);
+ }
+ );
+
+ for(auto& d : worker->output_data)
+ {
+ fstWriterEmitTimeChange(fstfile, d.first);
+ for (auto &data : d.second)
+ {
+ if (!use_signal.at(data.first)) continue;
+ Const value = data.second;
+ 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(fstfile, mapping[data.first], ss.str().c_str());
+ }
+ }
+ }
+
+ struct fstContext *fstfile = nullptr;
+ std::map<int,fstHandle> mapping;
+};
+
+struct AIWWriter : public OutputWriter
+{
+ AIWWriter(SimWorker *worker, std::string filename) : OutputWriter(worker) {
+ aiwfile.open(filename.c_str());
+ }
+
+ virtual ~AIWWriter()
+ {
+ aiwfile << '.' << '\n';
+ }
+
+ void write(std::map<int, bool> &) override
+ {
+ if (!aiwfile.is_open()) return;
+ if (worker->map_filename.empty())
+ log_cmd_error("For AIGER witness file map parameter is mandatory.\n");
+
+ std::ifstream mf(worker->map_filename);
+ std::string type, symbol;
+ int variable, index;
+ if (mf.fail())
+ log_cmd_error("Not able to read AIGER witness map file.\n");
+ while (mf >> type >> variable >> index >> symbol) {
+ RTLIL::IdString escaped_s = RTLIL::escape_id(symbol);
+ Wire *w = worker->top->module->wire(escaped_s);
+ if (!w)
+ log_error("Wire %s not present in module %s\n",log_id(escaped_s),log_id(worker->top->module));
+ if (index < w->start_offset || index > w->start_offset + w->width)
+ log_error("Index %d for wire %s is out of range\n", index, log_signal(w));
+ if (type == "input") {
+ aiw_inputs[variable] = SigBit(w,index);
+ } else if (type == "init") {
+ aiw_inits[variable] = SigBit(w,index);
+ } else if (type == "latch") {
+ aiw_latches[variable] = {SigBit(w,index), false};
+ } else if (type == "invlatch") {
+ aiw_latches[variable] = {SigBit(w,index), true};
}
}
- write_output_step(10*cycle);
- write_output_end();
+
+ worker->top->write_output_header(
+ [](IdString) {},
+ []() {},
+ [this](Wire *wire, int id) { mapping[wire] = id; }
+ );
+
+ std::map<int, Yosys::RTLIL::Const> current;
+ bool first = true;
+ for(auto& d : worker->output_data)
+ {
+ for (auto &data : d.second)
+ {
+ current[data.first] = data.second;
+ }
+ if (first) {
+ for (int i = 0;; i++)
+ {
+ if (aiw_latches.count(i)) {
+ SigBit bit = aiw_latches.at(i).first;
+ auto v = current[mapping[bit.wire]].bits.at(bit.offset);
+ if (v == State::S1)
+ aiwfile << (aiw_latches.at(i).second ? '0' : '1');
+ else
+ aiwfile << (aiw_latches.at(i).second ? '1' : '0');
+ continue;
+ }
+ aiwfile << '\n';
+ break;
+ }
+ first = false;
+ }
+
+ for (int i = 0;; i++)
+ {
+ if (aiw_inputs.count(i)) {
+ SigBit bit = aiw_inputs.at(i);
+ auto v = current[mapping[bit.wire]].bits.at(bit.offset);
+ if (v == State::S1)
+ aiwfile << '1';
+ else
+ aiwfile << '0';
+ continue;
+ }
+ if (aiw_inits.count(i)) {
+ SigBit bit = aiw_inits.at(i);
+ auto v = current[mapping[bit.wire]].bits.at(bit.offset);
+ if (v == State::S1)
+ aiwfile << '1';
+ else
+ aiwfile << '0';
+ continue;
+ }
+ aiwfile << '\n';
+ break;
+ }
+ }
}
+
+ std::ofstream aiwfile;
+ dict<int, std::pair<SigBit, bool>> aiw_latches;
+ dict<int, SigBit> aiw_inputs, aiw_inits;
+ std::map<Wire*,int> mapping;
};
struct SimPass : public Pass {
@@ -1247,6 +1566,16 @@ struct SimPass : public Pass {
log(" -fst <filename>\n");
log(" write the simulation results to the given FST file\n");
log("\n");
+ log(" -aiw <filename>\n");
+ log(" write the simulation results to an AIGER witness file\n");
+ log(" (requires a *.aim file via -map)\n");
+ log("\n");
+ log(" -x\n");
+ log(" ignore constant x outputs in simulation file.\n");
+ log("\n");
+ log(" -date\n");
+ log(" include date and full version info in output.\n");
+ log("\n");
log(" -clock <portname>\n");
log(" name of top-level clock input\n");
log("\n");
@@ -1283,7 +1612,7 @@ struct SimPass : public Pass {
log(" -map <filename>\n");
log(" read file with port and latch symbols, needed for AIGER witness input\n");
log("\n");
- log(" -scope\n");
+ log(" -scope <name>\n");
log(" scope of simulation top model\n");
log("\n");
log(" -at <time>\n");
@@ -1307,10 +1636,20 @@ struct SimPass : public Pass {
log(" -sim-gate\n");
log(" co-simulation, x in FST can match any value in simulation\n");
log("\n");
+ log(" -q\n");
+ log(" disable per-cycle/sample log message\n");
+ log("\n");
log(" -d\n");
log(" enable debug output\n");
log("\n");
}
+
+
+ static std::string file_base_name(std::string const & path)
+ {
+ return path.substr(path.find_last_of("/\\") + 1);
+ }
+
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
SimWorker worker;
@@ -1324,13 +1663,19 @@ struct SimPass : public Pass {
if (args[argidx] == "-vcd" && argidx+1 < args.size()) {
std::string vcd_filename = args[++argidx];
rewrite_filename(vcd_filename);
- worker.vcdfile.open(vcd_filename.c_str());
+ worker.outputfiles.emplace_back(std::unique_ptr<VCDWriter>(new VCDWriter(&worker, 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);
+ worker.outputfiles.emplace_back(std::unique_ptr<FSTWriter>(new FSTWriter(&worker, fst_filename.c_str())));
+ continue;
+ }
+ if (args[argidx] == "-aiw" && argidx+1 < args.size()) {
+ std::string aiw_filename = args[++argidx];
+ rewrite_filename(aiw_filename);
+ worker.outputfiles.emplace_back(std::unique_ptr<AIWWriter>(new AIWWriter(&worker, aiw_filename.c_str())));
continue;
}
if (args[argidx] == "-n" && argidx+1 < args.size()) {
@@ -1366,6 +1711,10 @@ struct SimPass : public Pass {
worker.hide_internal = false;
continue;
}
+ if (args[argidx] == "-q") {
+ worker.verbose = false;
+ continue;
+ }
if (args[argidx] == "-d") {
worker.debug = true;
continue;
@@ -1426,6 +1775,14 @@ struct SimPass : public Pass {
worker.sim_mode = SimulationMode::gate;
continue;
}
+ if (args[argidx] == "-x") {
+ worker.ignore_x = true;
+ continue;
+ }
+ if (args[argidx] == "-date") {
+ worker.date = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -1450,11 +1807,21 @@ struct SimPass : public Pass {
if (worker.sim_filename.empty())
worker.run(top_mod, numcycles);
- else
- if (worker.map_filename.empty())
- worker.run_cosim(top_mod, numcycles);
- else
- worker.run_cosim_witness(top_mod);
+ else {
+ std::string filename_trim = file_base_name(worker.sim_filename);
+ if (filename_trim.size() > 4 && ((filename_trim.compare(filename_trim.size()-4, std::string::npos, ".fst") == 0) ||
+ filename_trim.compare(filename_trim.size()-4, std::string::npos, ".vcd") == 0)) {
+ worker.run_cosim_fst(top_mod, numcycles);
+ } else if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".aiw") == 0) {
+ if (worker.map_filename.empty())
+ log_cmd_error("For AIGER witness file map parameter is mandatory.\n");
+ worker.run_cosim_aiger_witness(top_mod);
+ } else if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".wit") == 0) {
+ worker.run_cosim_btor2_witness(top_mod);
+ } else {
+ log_cmd_error("Unhandled extension for simulation input file `%s`.\n", worker.sim_filename.c_str());
+ }
+ }
}
} SimPass;
diff --git a/techlibs/gowin/cells_sim.v b/techlibs/gowin/cells_sim.v
index cc2ad3df5..b07ee6924 100644
--- a/techlibs/gowin/cells_sim.v
+++ b/techlibs/gowin/cells_sim.v
@@ -550,7 +550,6 @@ module GND(output G);
assign G = 0;
endmodule
-(* abc9_box *)
module IBUF(output O, input I);
specify
@@ -560,7 +559,6 @@ module IBUF(output O, input I);
assign O = I;
endmodule
-(* abc9_box *)
module OBUF(output O, input I);
specify
@@ -584,6 +582,14 @@ module IOBUF (O, IO, I, OEN);
assign I = IO;
endmodule
+module TLVDS_OBUF (I, O, OB);
+ input I;
+ output O;
+ output OB;
+ assign O = I;
+ assign OB = ~I;
+endmodule
+
module GSR (input GSRI);
wire GSRO = GSRI;
endmodule
diff --git a/techlibs/intel_alm/Makefile.inc b/techlibs/intel_alm/Makefile.inc
index 614d5802c..b5f279a92 100644
--- a/techlibs/intel_alm/Makefile.inc
+++ b/techlibs/intel_alm/Makefile.inc
@@ -19,6 +19,7 @@ $(eval $(call add_share_file,share/intel_alm/cyclonev,techlibs/intel_alm/cyclone
# RAM
$(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/bram_m10k.txt))
+$(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/bram_m10k_map.v))
$(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/bram_m20k.txt))
$(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/bram_m20k_map.v))
$(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/lutram_mlab.txt))
diff --git a/techlibs/intel_alm/common/bram_m10k.txt b/techlibs/intel_alm/common/bram_m10k.txt
index 0d9a49b7d..560711b65 100644
--- a/techlibs/intel_alm/common/bram_m10k.txt
+++ b/techlibs/intel_alm/common/bram_m10k.txt
@@ -1,4 +1,4 @@
-bram MISTRAL_M10K
+bram $__MISTRAL_M10K
init 0 # TODO: Re-enable when I figure out how BRAM init works
abits 13 @D8192x1
dbits 1 @D8192x1
@@ -21,7 +21,7 @@ bram MISTRAL_M10K
endbram
-match MISTRAL_M10K
+match $__MISTRAL_M10K
min efficiency 5
make_transp
endmatch
diff --git a/techlibs/intel_alm/common/bram_m10k_map.v b/techlibs/intel_alm/common/bram_m10k_map.v
new file mode 100644
index 000000000..8f9d4a3b3
--- /dev/null
+++ b/techlibs/intel_alm/common/bram_m10k_map.v
@@ -0,0 +1,16 @@
+// Stub to invert M10K write-enable.
+
+module \$__MISTRAL_M10K (CLK1, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
+
+parameter CFG_ABITS = 10;
+parameter CFG_DBITS = 10;
+
+input CLK1;
+input [CFG_ABITS-1:0] A1ADDR, B1ADDR;
+input [CFG_DBITS-1:0] A1DATA;
+input A1EN, B1EN;
+output reg [CFG_DBITS-1:0] B1DATA;
+
+MISTRAL_M10K #(.CFG_ABITS(CFG_ABITS), .CFG_DBITS(CFG_DBITS)) _TECHMAP_REPLACE_ (.CLK1(CLK1), .A1ADDR(A1ADDR), .A1DATA(A1DATA), .A1EN(!A1EN), .B1ADDR(B1ADDR), .B1DATA(B1DATA), .B1EN(B1EN));
+
+endmodule \ No newline at end of file
diff --git a/techlibs/intel_alm/common/mem_sim.v b/techlibs/intel_alm/common/mem_sim.v
index 370e17f27..c9ba8c7f1 100644
--- a/techlibs/intel_alm/common/mem_sim.v
+++ b/techlibs/intel_alm/common/mem_sim.v
@@ -145,7 +145,7 @@ endspecify
`endif
always @(posedge CLK1) begin
- if (A1EN)
+ if (!A1EN)
mem[(A1ADDR + 1) * CFG_DBITS - 1 : A1ADDR * CFG_DBITS] <= A1DATA;
if (B1EN)
diff --git a/techlibs/intel_alm/common/quartus_rename.v b/techlibs/intel_alm/common/quartus_rename.v
index 5850f6907..217dc5de9 100644
--- a/techlibs/intel_alm/common/quartus_rename.v
+++ b/techlibs/intel_alm/common/quartus_rename.v
@@ -157,6 +157,11 @@ output [CFG_DBITS-1:0] B1DATA;
// Much like the MLAB, the M10K has mem_init[01234] parameters which would let
// you initialise the RAM cell via hex literals. If they were implemented.
+// Since the MISTRAL_M10K block has an inverted write-enable (like the real hardware)
+// but the Quartus primitive expects a normal write-enable, we add an inverter.
+wire A1EN_N;
+NOT wren_inv (.IN(A1EN), .OUT(A1EN_N));
+
`RAM_BLOCK #(
.operation_mode("dual_port"),
.logical_ram_name(_TECHMAP_CELLNAME_),
@@ -176,10 +181,10 @@ output [CFG_DBITS-1:0] B1DATA;
.port_b_first_bit_number(0),
.port_b_address_clock("clock0"),
.port_b_read_enable_clock("clock0")
-) _TECHMAP_REPLACE_ (
+) ram_block (
.portaaddr(A1ADDR),
.portadatain(A1DATA),
- .portawe(A1EN),
+ .portawe(A1EN_N),
.portbaddr(B1ADDR),
.portbdataout(B1DATA),
.portbre(B1EN),
diff --git a/techlibs/intel_alm/synth_intel_alm.cc b/techlibs/intel_alm/synth_intel_alm.cc
index 34a5ffa5d..43d3592d5 100644
--- a/techlibs/intel_alm/synth_intel_alm.cc
+++ b/techlibs/intel_alm/synth_intel_alm.cc
@@ -262,8 +262,7 @@ struct SynthIntelALMPass : public ScriptPass {
if (!nobram && check_label("map_bram", "(skip if -nobram)")) {
run(stringf("memory_bram -rules +/intel_alm/common/bram_%s.txt", bram_type.c_str()));
- if (help_mode || bram_type != "m10k")
- run(stringf("techmap -map +/intel_alm/common/bram_%s_map.v", bram_type.c_str()));
+ run(stringf("techmap -map +/intel_alm/common/bram_%s_map.v", bram_type.c_str()));
}
if (!nolutram && check_label("map_lutram", "(skip if -nolutram)")) {
diff --git a/tests/arch/intel_alm/blockram.ys b/tests/arch/intel_alm/blockram.ys
index c157c3165..3b61b9339 100644
--- a/tests/arch/intel_alm/blockram.ys
+++ b/tests/arch/intel_alm/blockram.ys
@@ -2,5 +2,6 @@ read_verilog ../common/blockram.v
chparam -set ADDRESS_WIDTH 10 -set DATA_WIDTH 10 sync_ram_sdp
synth_intel_alm -family cyclonev -noiopad -noclkbuf
cd sync_ram_sdp
+select -assert-count 1 t:MISTRAL_NOT
select -assert-count 1 t:MISTRAL_M10K
-select -assert-none t:MISTRAL_M10K %% t:* %D
+select -assert-none t:MISTRAL_NOT t:MISTRAL_M10K %% t:* %D
diff --git a/tests/sat/grom_cpu.v b/tests/sat/grom_cpu.v
index f9fef043b..914c0f56c 100644
--- a/tests/sat/grom_cpu.v
+++ b/tests/sat/grom_cpu.v
@@ -185,7 +185,7 @@ module grom_cpu(
`ifdef DISASSEMBLY
$display("INC R%d",IR[1:0]);
`endif
- alu_op <= 4'b0001; // ALU_OP_ADD
+ alu_op <= 4'b0000; // ALU_OP_ADD
end
2'b01 : begin
`ifdef DISASSEMBLY
diff --git a/tests/sat/ram_memory.v b/tests/sat/ram_memory.v
index 053ef206c..0d91514b2 100644
--- a/tests/sat/ram_memory.v
+++ b/tests/sat/ram_memory.v
@@ -27,6 +27,8 @@ module ram_memory(
store[256] <= 8'b11010001; // OUT [0],R1
store[257] <= 8'b00000000; //
store[258] <= 8'b01111110; // RET
+
+ store[512] <= 8'b00000000;
end
always @(posedge clk)
diff --git a/tests/various/.gitignore b/tests/various/.gitignore
index 2bb6c7179..c6373468a 100644
--- a/tests/various/.gitignore
+++ b/tests/various/.gitignore
@@ -5,3 +5,4 @@
/run-test.mk
/plugin.so
/plugin.so.dSYM
+/temp
diff --git a/tests/various/json_escape_chars.ys b/tests/various/json_escape_chars.ys
new file mode 100644
index 000000000..f118357c0
--- /dev/null
+++ b/tests/various/json_escape_chars.ys
@@ -0,0 +1,14 @@
+! mkdir -p temp
+read_verilog <<EOT
+(* src = "\042 \057 \134 \010 \014 \012 \015 \011 \025 \033" *)
+module foo;
+endmodule
+EOT
+write_json temp/test_escapes.json
+design -reset
+read_json temp/test_escapes.json
+write_json temp/test_escapes.json
+design -reset
+read_json temp/test_escapes.json
+write_rtlil temp/test_escapes.json.il
+! grep -F 'attribute \src "\" / \\ \010 \014 \n \015 \t \025 \033"' temp/test_escapes.json.il