diff options
author | Archie <ac11018@ic.ac.uk> | 2022-08-21 17:18:20 -0500 |
---|---|---|
committer | Archie <ac11018@ic.ac.uk> | 2022-08-21 17:18:20 -0500 |
commit | db73f3c26b2768f93c7573b7c7d74b1cc7a0756d (patch) | |
tree | 81696fd98770e519aea96fe3a6e40bcc3b3a4360 | |
parent | e7e8e3b0f65ea1ebfcf04bffd0d9ba90f8e0d7fe (diff) | |
parent | 029c2785e810fda0ccc5abbb6057af760f2fc6f3 (diff) | |
download | yosys-db73f3c26b2768f93c7573b7c7d74b1cc7a0756d.tar.gz yosys-db73f3c26b2768f93c7573b7c7d74b1cc7a0756d.tar.bz2 yosys-db73f3c26b2768f93c7573b7c7d74b1cc7a0756d.zip |
Merge branch 'master' of https://github.com/ALGCDG/yosys
81 files changed, 4859 insertions, 737 deletions
diff --git a/.github/workflows/test-macos.yml b/.github/workflows/test-macos.yml index b14ce8633..22cf5e658 100644 --- a/.github/workflows/test-macos.yml +++ b/.github/workflows/test-macos.yml @@ -71,57 +71,3 @@ jobs: shell: bash run: | make -j${{ env.procs }} test CXXSTD=${{ matrix.cpp_std }} CC=cc CXX=cc LD=cc - - - test-macos-homebrew: - runs-on: ${{ matrix.os.id }} - strategy: - matrix: - os: - - { id: macos-10.15, name: Catalina } - cpp_std: - - 'c++17' - compiler: - - gcc - fail-fast: false - steps: - - name: Install Dependencies - run: | - brew install bison flex gawk libffi pkg-config bash - - - name: Runtime environment - shell: bash - env: - WORKSPACE: ${{ github.workspace }} - run: | - echo "GITHUB_WORKSPACE=`pwd`" >> $GITHUB_ENV - echo "$GITHUB_WORKSPACE/.local/bin" >> $GITHUB_PATH - echo "$(brew --prefix bison)/bin" >> $GITHUB_PATH - echo "$(brew --prefix flex)/bin" >> $GITHUB_PATH - echo "procs=$(sysctl -n hw.ncpu)" >> $GITHUB_ENV - - - name: Setup compiler - shell: bash - run: | - brew install ${{ matrix.compiler }} - CC=${COMPILER/@/-} - CXX=${CC/#gcc/g++} - echo "CC=$CC" >> $GITHUB_ENV - echo "CXX=$CXX" >> $GITHUB_ENV - env: - COMPILER: ${{ matrix.compiler }} - - - name: Tool versions - shell: bash - run: | - $CC --version - $CXX --version - - - name: Checkout Yosys - uses: actions/checkout@v2 - - - name: Build yosys - shell: bash - run: | - make config-gcc - make -j${{ env.procs }} CXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC diff --git a/.gitignore b/.gitignore index 0460c7c13..49b886e7e 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,9 @@ __pycache__ /yosys-smtbmc /yosys-smtbmc.exe /yosys-smtbmc-script.py +/yosys-witness +/yosys-witness.exe +/yosys-witness-script.py /yosys-filterlib /yosys-filterlib.exe /kernel/*.pyh @@ -2,8 +2,61 @@ List of major changes and improvements between releases ======================================================= -Yosys 0.18 .. Yosys 0.18-dev +Yosys 0.20 .. Yosys 0.20-dev -------------------------- + * New commands and options + - Added "formalff" pass - transforms FFs for formal verification + - Added option "-formal" to "memory_map" pass + - Added option "-witness" to "rename" - give public names to all signals + present in yosys witness traces + - Added option "-hdlname" to "sim" pass - preserves hiearachy when writing + simulation output for a flattened design + + * Formal Verification + - Added $anyinit cell to directly represent FFs with an unconstrained + initialization value. These can be generated by the new formalff pass. + - New JSON based yosys witness format for formal verification traces. + - yosys-smtbmc: Reading and writing of yosys witness traces. + - write_smt2: Emit inline metadata to support yosys witness trace. + - yosys-witness is a new tool to inspect and convert yosys witness traces. + - write_aiger: Option to write a map file for yosys witness trace + conversion. + - yosys-witness: Conversion from and to AIGER witness traces. + +Yosys 0.19 .. Yosys 0.20 +-------------------------- + * New commands and options + - Added option "-wb" to "read_liberty" pass + + * Various + - Added support for $modfloor operator to cxxrtl backend + - Support build on OpenBSD + - Fixed smt2 backend use of $shift/$shiftx with negative shift amounts, + which affects bit/part-select assignments with a dynamic index. Shift + operators were not affected. + + * Verific support + - Proper import of port ranges into Yosys, may result in reversed + bit-order of top-level ports for some synthesis flows. + +Yosys 0.18 .. Yosys 0.19 +-------------------------- + * New commands and options + - Added option "-rom-only" to "memory_libmap" pass + - Added option "-smtcheck" to "hierarchy" pass + - Added option "-keepdc" to "memory_libmap" pass + - Added option "-suffix" to "rename" pass + - Added "gatemate_foldinv" pass + + * Formal Verification + - Added support for $pos cell in btor backend + - Added the "smtlib2_module" and "smtlib2_comb_expr" attributes + + * GateMate support + - Added LUT tree mapping + + * Verific support + - Added option "-pp" to "verific -import" Yosys 0.17 .. Yosys 0.18 -------------------------- diff --git a/CODEOWNERS b/CODEOWNERS index 11a8cc026..a33a9a68c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -16,7 +16,7 @@ backends/cxxrtl/ @whitequark passes/cmds/bugpoint.cc @whitequark passes/techmap/flowmap.cc @whitequark passes/opt/opt_lut.cc @whitequark -passes/techmap/abc9*.cc @eddiehung +passes/techmap/abc9*.cc @eddiehung @Ravenslofty backends/aiger/xaiger.cc @eddiehung @@ -30,7 +30,7 @@ backends/aiger/xaiger.cc @eddiehung frontends/verilog/ @zachjs frontends/ast/ @zachjs -techlibs/intel_alm/ @ZirconiumX +techlibs/intel_alm/ @Ravenslofty techlibs/gowin/ @pepijndevos techlibs/gatemate/ @pu-cc @@ -126,10 +126,12 @@ endif else LDFLAGS += -rdynamic +ifneq ($(OS), OpenBSD) LDLIBS += -lrt endif +endif -YOSYS_VER := 0.18+10 +YOSYS_VER := 0.20+45 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo @@ -145,7 +147,7 @@ endif OBJS = kernel/version_$(GIT_REV).o bumpversion: - sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 19ce3b4.. | wc -l`/;" Makefile + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 4fcb95e.. | wc -l`/;" Makefile # set 'ABCREV = default' to use abc/ as it is # @@ -153,10 +155,10 @@ 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 = 09a7e6d +ABCREV = 20f970f ABCPULL = 1 ABCURL ?= https://github.com/YosysHQ/abc -ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 VERBOSE=$(Q) +ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) # set ABCEXTERNAL = <abc-command> to use an external ABC instance # Note: The in-tree ABC (yosys-abc) will not be installed when ABCEXTERNAL is set. @@ -197,11 +199,16 @@ endif endif +ABC_ARCHFLAGS = "" +ifeq ($(OS), OpenBSD) +ABC_ARCHFLAGS += "-DABC_NO_RLIMIT" +endif + ifeq ($(CONFIG),clang) CXX = clang LD = clang++ CXXFLAGS += -std=$(CXXSTD) -Os -ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -Wno-c++11-narrowing $(ABC_ARCHFLAGS)" ifneq ($(SANITIZER),) $(info [Clang Sanitizer] $(SANITIZER)) @@ -224,7 +231,7 @@ else ifeq ($(CONFIG),gcc) CXX = gcc LD = gcc CXXFLAGS += -std=$(CXXSTD) -Os -ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)" else ifeq ($(CONFIG),gcc-static) LD = $(CXX) @@ -260,7 +267,7 @@ else ifeq ($(CONFIG),emcc) CXX = emcc LD = emcc CXXFLAGS := -std=$(CXXSTD) $(filter-out -fPIC -ggdb,$(CXXFLAGS)) -ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DABC_MEMALIGN=8" +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DABC_MEMALIGN=8 -Wno-c++11-narrowing" EMCC_CXXFLAGS := -Os -Wno-warn-absolute-paths EMCC_LDFLAGS := --memory-init-file 0 --embed-file share EMCC_LDFLAGS += -s NO_EXIT_RUNTIME=1 @@ -314,7 +321,7 @@ CXXFLAGS := $(WASIFLAGS) -std=$(CXXSTD) -Os $(filter-out -fPIC,$(CXXFLAGS)) LDFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LDFLAGS)) LDLIBS := $(filter-out -lrt,$(LDLIBS)) ABCMKARGS += AR="$(AR)" RANLIB="$(RANLIB)" -ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING" +ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING -DABC_NO_RLIMIT -Wno-c++11-narrowing" ABCMKARGS += OPTFLAGS="-Os" EXE = .wasm @@ -395,7 +402,7 @@ endif # ENABLE_PYOSYS ifeq ($(ENABLE_READLINE),1) CXXFLAGS += -DYOSYS_ENABLE_READLINE -ifeq ($(OS), FreeBSD) +ifeq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD)) CXXFLAGS += -I/usr/local/include endif LDLIBS += -lreadline @@ -430,7 +437,7 @@ endif ifeq ($(ENABLE_PLUGINS),1) CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags libffi) -DYOSYS_ENABLE_PLUGINS LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi) -ifneq ($(OS), FreeBSD) +ifneq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD)) LDLIBS += -ldl endif endif @@ -447,10 +454,13 @@ endif ifeq ($(ENABLE_TCL),1) TCL_VERSION ?= tcl$(shell bash -c "tclsh <(echo 'puts [info tclversion]')") -ifeq ($(OS), FreeBSD) +ifeq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD)) +# BSDs usually use tcl8.6, but the lib is named "libtcl86" TCL_INCLUDE ?= /usr/local/include/$(TCL_VERSION) +TCL_LIBS ?= -l$(subst .,,$(TCL_VERSION)) else TCL_INCLUDE ?= /usr/include/$(TCL_VERSION) +TCL_LIBS ?= -l$(TCL_VERSION) endif ifeq ($(CONFIG),mxe) @@ -458,12 +468,7 @@ CXXFLAGS += -DYOSYS_ENABLE_TCL LDLIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz -luserenv else CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags tcl || echo -I$(TCL_INCLUDE)) -DYOSYS_ENABLE_TCL -ifeq ($(OS), FreeBSD) -# FreeBSD uses tcl8.6, but lib is named "libtcl86" -LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo -l$(TCL_VERSION) | tr -d '.') -else -LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo -l$(TCL_VERSION)) -endif +LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo $(TCL_LIBS)) endif endif @@ -772,11 +777,16 @@ ifneq ($(ABCREV),default) $(Q) if test -d abc/.hg; then \ echo 'REEBE: NOP qverpgbel vf n ut jbexvat pbcl! Erzbir nop/ naq er-eha "znxr".' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \ fi - $(Q) if test -d abc && ! git -C abc diff-index --quiet HEAD; then \ + $(Q) if test -d abc && test -d abc/.git && ! git -C abc diff-index --quiet HEAD; then \ echo 'REEBE: NOP pbagnvaf ybpny zbqvsvpngvbaf! Frg NOPERI=qrsnhyg va Lbflf Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \ fi + $(Q) if test -d abc && ! test -d abc/.git && ! test "`cat abc/.gitcommit | cut -c1-7`" == "$(ABCREV)"; then \ + echo 'REEBE: Qbjaybnqrq NOP irefvbaf qbrf abg zngpu! Qbjaybnq sebz:' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; echo $(ABCURL)/archive/$(ABCREV).tar.gz; false; \ + fi # set a variable so the test fails if git fails to run - when comparing outputs directly, empty string would match empty string - $(Q) if ! (cd abc 2> /dev/null && rev="`git rev-parse $(ABCREV)`" && test "`git rev-parse HEAD`" == "$$rev"); then \ + $(Q) if test -d abc && ! test -d abc/.git && test "`cat abc/.gitcommit | cut -c1-7`" == "$(ABCREV)"; then \ + echo "Compiling local copy of ABC"; \ + elif ! (cd abc 2> /dev/null && rev="`git rev-parse $(ABCREV)`" && test "`git rev-parse HEAD`" == "$$rev"); then \ test $(ABCPULL) -ne 0 || { echo 'REEBE: NOP abg hc gb qngr naq NOPCHYY frg gb 0 va Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; exit 1; }; \ echo "Pulling ABC from $(ABCURL):"; set -x; \ test -d abc || git clone $(ABCURL) abc; \ @@ -505,6 +505,18 @@ Verilog Attributes and non-standard features module. Modules with such cells will be reprocessed during the ``hierarchy`` pass once the referenced module definition(s) become available. +- The ``smtlib2_module`` attribute can be set on a blackbox module to specify a + formal model directly using SMT-LIB 2. For such a module, the + ``smtlib2_comb_expr`` attribute can be used on output ports to define their + value using an SMT-LIB 2 expression. For example: + + (* blackbox *) + (* smtlib2_module *) + module submod(a, b); + input [7:0] a; + (* smtlib2_comb_expr = "(bvnot a)" *) + output [7:0] b; + endmodule Non-standard or SystemVerilog features for formal verification ============================================================== diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc index 547d131ee..513f9d95a 100644 --- a/backends/aiger/aiger.cc +++ b/backends/aiger/aiger.cc @@ -19,6 +19,7 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "libs/json11/json11.hpp" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -61,6 +62,8 @@ struct AigerWriter dict<SigBit, int> init_inputs; int initstate_ff = 0; + dict<SigBit, int> ywmap_clocks; + int mkgate(int a0, int a1) { aig_m++, aig_a++; @@ -159,6 +162,17 @@ struct AigerWriter output_bits.insert(wirebit); } } + + if (wire->width == 1) { + auto gclk_attr = wire->attributes.find(ID::replaced_by_gclk); + if (gclk_attr != wire->attributes.end()) { + SigBit bit = sigmap(wire); + if (gclk_attr->second == State::S1) + ywmap_clocks[bit] |= 1; + else if (gclk_attr->second == State::S0) + ywmap_clocks[bit] |= 2; + } + } } for (auto bit : input_bits) @@ -186,6 +200,22 @@ struct AigerWriter unused_bits.erase(D); undriven_bits.erase(Q); ff_map[Q] = D; + + if (cell->type != ID($_FF_)) { + auto sig_clk = sigmap(cell->getPort(ID::C).as_bit()); + ywmap_clocks[sig_clk] |= cell->type == ID($_DFF_N_) ? 2 : 1; + } + continue; + } + + if (cell->type == ID($anyinit)) + { + auto sig_d = sigmap(cell->getPort(ID::D)); + auto sig_q = sigmap(cell->getPort(ID::Q)); + for (int i = 0; i < sig_d.size(); i++) { + undriven_bits.erase(sig_q[i]); + ff_map[sig_q[i]] = sig_d[i]; + } continue; } @@ -678,6 +708,137 @@ struct AigerWriter for (auto &it : wire_lines) f << it.second; } + + template<class T> static std::vector<std::string> witness_path(T *obj) { + std::vector<std::string> path; + if (obj->name.isPublic()) { + auto hdlname = obj->get_string_attribute(ID::hdlname); + for (auto token : split_tokens(hdlname)) + path.push_back("\\" + token); + } + if (path.empty()) + path.push_back(obj->name.str()); + return path; + } + + void write_ywmap(std::ostream &f) + { + f << "{\n"; + f << " \"version\": \"Yosys Witness Aiger Map\",\n"; + f << stringf(" \"generator\": %s,\n", json11::Json(yosys_version_str).dump().c_str()); + f << stringf(" \"latch_count\": %d,\n", aig_l); + f << stringf(" \"input_count\": %d,\n", aig_i); + + dict<int, string> clock_lines; + dict<int, string> input_lines; + dict<int, string> init_lines; + dict<int, string> seq_lines; + + for (auto cell : module->cells()) + { + if (cell->type.in(ID($_FF_), ID($_DFF_N_), ID($_DFF_P_), ID($anyinit), ID($anyconst), ID($anyseq))) + { + // Use sig_q to get the FF output name, but sig to lookup aiger bits + auto sig_qy = cell->getPort(cell->type.in(ID($anyconst), ID($anyseq)) ? ID::Y : ID::Q); + SigSpec sig = sigmap(sig_qy); + + for (int i = 0; i < GetSize(sig_qy); i++) { + if (sig_qy[i].wire == nullptr || sig[i].wire == nullptr) + continue; + + auto wire = sig_qy[i].wire; + + if (init_inputs.count(sig[i])) { + int a = init_inputs.at(sig[i]); + log_assert((a & 1) == 0); + init_lines[a] += json11::Json(json11::Json::object { + { "path", witness_path(wire) }, + { "input", (a >> 1) - 1 }, + { "offset", sig_qy[i].offset }, + }).dump(); + } + + if (input_bits.count(sig[i])) { + int a = aig_map.at(sig[i]); + log_assert((a & 1) == 0); + seq_lines[a] += json11::Json(json11::Json::object { + { "path", witness_path(wire) }, + { "input", (a >> 1) - 1 }, + { "offset", sig_qy[i].offset }, + }).dump(); + } + } + } + } + + for (auto wire : module->wires()) + { + SigSpec sig = sigmap(wire); + if (wire->port_input) + { + auto path = witness_path(wire); + for (int i = 0; i < GetSize(wire); i++) { + if (aig_map.count(sig[i]) == 0 || sig[i].wire == nullptr) + continue; + + int a = aig_map.at(sig[i]); + log_assert((a & 1) == 0); + input_lines[a] += json11::Json(json11::Json::object { + { "path", path }, + { "input", (a >> 1) - 1 }, + { "offset", i }, + }).dump(); + + if (ywmap_clocks.count(sig[i])) { + int clock_mode = ywmap_clocks[sig[i]]; + if (clock_mode != 3) { + clock_lines[a] += json11::Json(json11::Json::object { + { "path", path }, + { "input", (a >> 1) - 1 }, + { "offset", i }, + { "edge", clock_mode == 1 ? "posedge" : "negedge" }, + }).dump(); + } + } + } + } + } + + f << " \"clocks\": ["; + clock_lines.sort(); + const char *sep = "\n "; + for (auto &it : clock_lines) { + f << sep << it.second; + sep = ",\n "; + } + f << "\n ],\n"; + + f << " \"inputs\": ["; + input_lines.sort(); + sep = "\n "; + for (auto &it : input_lines) { + f << sep << it.second; + sep = ",\n "; + } + f << "\n ],\n"; + + f << " \"seqs\": ["; + sep = "\n "; + for (auto &it : seq_lines) { + f << sep << it.second; + sep = ",\n "; + } + f << "\n ],\n"; + + f << " \"inits\": ["; + sep = "\n "; + for (auto &it : init_lines) { + f << sep << it.second; + sep = ",\n "; + } + f << "\n ]\n}\n"; + } + }; struct AigerBackend : public Backend { @@ -717,6 +878,9 @@ struct AigerBackend : public Backend { log(" -no-startoffset\n"); log(" make indexes zero based, enable using map files with smt solvers.\n"); log("\n"); + log(" -ywmap <filename>\n"); + log(" write a map file for conversion to and from yosys witness traces.\n"); + log("\n"); log(" -I, -O, -B, -L\n"); log(" If the design contains no input/output/assert/flip-flop then create one\n"); log(" dummy input/output/bad_state-pin or latch to make the tools reading the\n"); @@ -736,6 +900,7 @@ struct AigerBackend : public Backend { bool lmode = false; bool no_startoffset = false; std::string map_filename; + std::string yw_map_filename; log_header(design, "Executing AIGER backend.\n"); @@ -767,6 +932,10 @@ struct AigerBackend : public Backend { verbose_map = true; continue; } + if (yw_map_filename.empty() && args[argidx] == "-ywmap" && argidx+1 < args.size()) { + yw_map_filename = args[++argidx]; + continue; + } if (args[argidx] == "-no-startoffset") { no_startoffset = true; continue; @@ -791,6 +960,9 @@ struct AigerBackend : public Backend { } extra_args(f, filename, args, argidx, !ascii_mode); + if (!yw_map_filename.empty() && !zinit_mode) + log_error("Currently -ywmap requires -zinit.\n"); + Module *top_module = design->top_module(); if (top_module == nullptr) @@ -815,6 +987,14 @@ struct AigerBackend : public Backend { log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno)); writer.write_map(mapf, verbose_map, no_startoffset); } + + if (!yw_map_filename.empty()) { + std::ofstream mapf; + mapf.open(yw_map_filename.c_str(), std::ofstream::trunc); + if (mapf.fail()) + log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno)); + writer.write_ywmap(mapf); + } } } AigerBackend; diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index 7de5deadd..06de71018 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -446,25 +446,28 @@ struct BtorWorker goto okay; } - if (cell->type.in(ID($not), ID($neg), ID($_NOT_))) + if (cell->type.in(ID($not), ID($neg), ID($_NOT_), ID($pos))) { string btor_op; if (cell->type.in(ID($not), ID($_NOT_))) btor_op = "not"; if (cell->type == ID($neg)) btor_op = "neg"; - log_assert(!btor_op.empty()); int width = std::max(GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::Y))); bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false; - - int sid = get_bv_sid(width); int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed); - - int nid = next_nid++; - btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); - SigSpec sig = sigmap(cell->getPort(ID::Y)); + // the $pos cell just passes through, all other cells need an actual operation applied + int nid = nid_a; + if (cell->type != ID($pos)) + { + log_assert(!btor_op.empty()); + int sid = get_bv_sid(width); + nid = next_nid++; + btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); + } + if (GetSize(sig) < width) { int sid = get_bv_sid(GetSize(sig)); int nid2 = next_nid++; @@ -609,7 +612,7 @@ struct BtorWorker goto okay; } - if (cell->type.in(ID($dff), ID($ff), ID($_DFF_P_), ID($_DFF_N), ID($_FF_))) + if (cell->type.in(ID($dff), ID($ff), ID($anyinit), ID($_DFF_P_), ID($_DFF_N), ID($_FF_))) { SigSpec sig_d = sigmap(cell->getPort(ID::D)); SigSpec sig_q = sigmap(cell->getPort(ID::Q)); @@ -1109,6 +1112,16 @@ struct BtorWorker btorf("%d input %d%s\n", nid, sid, getinfo(wire).c_str()); add_nid_sig(nid, sig); + + if (!info_filename.empty()) { + auto gclk_attr = wire->attributes.find(ID::replaced_by_gclk); + if (gclk_attr != wire->attributes.end()) { + if (gclk_attr->second == State::S1) + info_clocks[nid] |= 1; + else if (gclk_attr->second == State::S0) + info_clocks[nid] |= 2; + } + } } btorf_pop("inputs"); diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h index b4ffa87cd..073921cc4 100644 --- a/backends/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/cxxrtl.h @@ -1575,6 +1575,27 @@ value<BitsY> mod_ss(const value<BitsA> &a, const value<BitsB> &b) { return divmod_ss<BitsY>(a, b).second; } +template<size_t BitsY, size_t BitsA, size_t BitsB> +CXXRTL_ALWAYS_INLINE +value<BitsY> modfloor_uu(const value<BitsA> &a, const value<BitsB> &b) { + return divmod_uu<BitsY>(a, b).second; +} + +// GHDL Modfloor operator. Returns r=a mod b, such that r has the same sign as b and +// a=b*N+r where N is some integer +// In practical terms, when a and b have different signs and the remainder returned by divmod_ss is not 0 +// then return the remainder + b +template<size_t BitsY, size_t BitsA, size_t BitsB> +CXXRTL_ALWAYS_INLINE +value<BitsY> modfloor_ss(const value<BitsA> &a, const value<BitsB> &b) { + value<BitsY> r; + r = divmod_ss<BitsY>(a, b).second; + if((b.is_neg() != a.is_neg()) && !r.is_zero()) + return add_ss<BitsY>(b, r); + return r; +} + + // Memory helper struct memory_index { bool valid; diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index 404755b1e..62768bd33 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -185,7 +185,7 @@ bool is_binary_cell(RTLIL::IdString type) ID($and), ID($or), ID($xor), ID($xnor), ID($logic_and), ID($logic_or), ID($shl), ID($sshl), ID($shr), ID($sshr), ID($shift), ID($shiftx), ID($eq), ID($ne), ID($eqx), ID($nex), ID($gt), ID($ge), ID($lt), ID($le), - ID($add), ID($sub), ID($mul), ID($div), ID($mod)); + ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($modfloor)); } bool is_extending_cell(RTLIL::IdString type) diff --git a/backends/jny/jny.cc b/backends/jny/jny.cc index d801b144c..2b8d51b76 100644 --- a/backends/jny/jny.cc +++ b/backends/jny/jny.cc @@ -27,6 +27,8 @@ #include <algorithm> #include <unordered_map> #include <vector> +#include <sstream> +#include <iterator> USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -116,17 +118,17 @@ struct JnyWriter _include_connections(connections), _include_attributes(attributes), _include_properties(properties) { } - void write_metadata(Design *design, uint16_t indent_level = 0) + void write_metadata(Design *design, uint16_t indent_level = 0, std::string invk = "") { log_assert(design != nullptr); design->sort(); f << "{\n"; + f << " \"$schema\": \"https://raw.githubusercontent.com/YosysHQ/yosys/master/misc/jny.schema.json\",\n"; f << stringf(" \"generator\": \"%s\",\n", escape_string(yosys_version_str).c_str()); - // XXX(aki): Replace this with a proper version info eventually:tm: - f << " \"version\": \"0.0.0\",\n"; - + f << " \"version\": \"0.0.1\",\n"; + f << " \"invocation\": \"" << escape_string(invk) << "\",\n"; f << " \"features\": ["; size_t fnum{0}; @@ -409,11 +411,12 @@ struct JnyWriter struct JnyBackend : public Backend { JnyBackend() : Backend("jny", "generate design metadata") { } void help() override { - // XXX(aki): TODO: explicitly document the JSON schema // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" jny [options] [selection]\n"); log("\n"); + log("Write JSON netlist metadata for the current design\n"); + log("\n"); log(" -no-connections\n"); log(" Don't include connection information in the netlist output.\n"); log("\n"); @@ -423,8 +426,8 @@ struct JnyBackend : public Backend { log(" -no-properties\n"); log(" Don't include property information in the netlist output.\n"); log("\n"); - log("Write a JSON metadata for the current design\n"); - log("\n"); + log("The JSON schema for JNY output files is located in the \"jny.schema.json\" file\n"); + log("which is located at \"https://raw.githubusercontent.com/YosysHQ/yosys/master/misc/jny.schema.json\"\n"); log("\n"); } @@ -453,12 +456,22 @@ struct JnyBackend : public Backend { break; } + + // Compose invocation line + std::ostringstream invk; + if (!args.empty()) { + std::copy(args.begin(), args.end(), + std::ostream_iterator<std::string>(invk, " ") + ); + } + invk << filename; + extra_args(f, filename, args, argidx); log_header(design, "Executing jny backend.\n"); JnyWriter jny_writer(*f, false, connections, attributes, properties); - jny_writer.write_metadata(design); + jny_writer.write_metadata(design, 0, invk.str()); } } JnyBackend; @@ -472,7 +485,7 @@ struct JnyPass : public Pass { log("\n"); log(" jny [options] [selection]\n"); log("\n"); - log("Write a JSON netlist metadata for the current design\n"); + log("Write JSON netlist metadata for the current design\n"); log("\n"); log(" -o <filename>\n"); log(" write to the specified file.\n"); @@ -520,6 +533,15 @@ struct JnyPass : public Pass { break; } + + // Compose invocation line + std::ostringstream invk; + if (!args.empty()) { + std::copy(args.begin(), args.end(), + std::ostream_iterator<std::string>(invk, " ") + ); + } + extra_args(args, argidx, design); std::ostream *f; @@ -534,13 +556,14 @@ struct JnyPass : public Pass { log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno)); } f = ff; + invk << filename; } else { f = &buf; } JnyWriter jny_writer(*f, false, connections, attributes, properties); - jny_writer.write_metadata(design); + jny_writer.write_metadata(design, 0, invk.str()); if (!filename.empty()) { delete f; diff --git a/backends/rtlil/rtlil_backend.cc b/backends/rtlil/rtlil_backend.cc index 1b11de5ec..b5163aefe 100644 --- a/backends/rtlil/rtlil_backend.cc +++ b/backends/rtlil/rtlil_backend.cc @@ -75,7 +75,7 @@ void RTLIL_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int wi else if (str[i] == '\t') f << stringf("\\t"); else if (str[i] < 32) - f << stringf("\\%03o", str[i]); + f << stringf("\\%03o", (unsigned char)str[i]); else if (str[i] == '"') f << stringf("\\\""); else if (str[i] == '\\') diff --git a/backends/smt2/Makefile.inc b/backends/smt2/Makefile.inc index fb01308bd..3afe990e7 100644 --- a/backends/smt2/Makefile.inc +++ b/backends/smt2/Makefile.inc @@ -7,6 +7,7 @@ ifneq ($(CONFIG),emcc) # MSYS targets support yosys-smtbmc, but require a launcher script ifeq ($(CONFIG),$(filter $(CONFIG),msys2 msys2-64)) TARGETS += $(PROGRAM_PREFIX)yosys-smtbmc.exe $(PROGRAM_PREFIX)yosys-smtbmc-script.py +TARGETS += $(PROGRAM_PREFIX)yosys-witness.exe $(PROGRAM_PREFIX)yosys-witness-script.py # Needed to find the Python interpreter for yosys-smtbmc scripts. # Override if necessary, it is only used for msys2 targets. PYTHON := $(shell cygpath -w -m $(PREFIX)/bin/python3) @@ -15,18 +16,31 @@ $(PROGRAM_PREFIX)yosys-smtbmc-script.py: backends/smt2/smtbmc.py $(P) sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)yosys/python3"]]|;' \ -e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@ +$(PROGRAM_PREFIX)yosys-witness-script.py: backends/smt2/witness.py + $(P) sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)yosys/python3"]]|;' \ + -e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@ + $(PROGRAM_PREFIX)yosys-smtbmc.exe: misc/launcher.c $(PROGRAM_PREFIX)yosys-smtbmc-script.py $(P) $(CXX) -DGUI=0 -O -s -o $@ $< + +$(PROGRAM_PREFIX)yosys-witness.exe: misc/launcher.c $(PROGRAM_PREFIX)yosys-witness-script.py + $(P) $(CXX) -DGUI=0 -O -s -o $@ $< # Other targets else -TARGETS += $(PROGRAM_PREFIX)yosys-smtbmc +TARGETS += $(PROGRAM_PREFIX)yosys-smtbmc $(PROGRAM_PREFIX)yosys-witness $(PROGRAM_PREFIX)yosys-smtbmc: backends/smt2/smtbmc.py $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)yosys/python3"]]|;' < $< > $@.new $(Q) chmod +x $@.new $(Q) mv $@.new $@ + +$(PROGRAM_PREFIX)yosys-witness: backends/smt2/witness.py + $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)yosys/python3"]]|;' < $< > $@.new + $(Q) chmod +x $@.new + $(Q) mv $@.new $@ endif $(eval $(call add_share_file,share/python3,backends/smt2/smtio.py)) +$(eval $(call add_share_file,share/python3,backends/smt2/ywio.py)) endif endif diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index 7481e0510..54783cf1b 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -23,6 +23,7 @@ #include "kernel/celltypes.h" #include "kernel/log.h" #include "kernel/mem.h" +#include "libs/json11/json11.hpp" #include <string> USING_YOSYS_NAMESPACE @@ -241,6 +242,17 @@ struct Smt2Worker for (auto wire : module->wires()) { + auto gclk_attr = wire->attributes.find(ID::replaced_by_gclk); + if (gclk_attr != wire->attributes.end()) { + if (gclk_attr->second == State::S1) + clock_posedge.insert(sigmap(wire)); + else if (gclk_attr->second == State::S0) + clock_negedge.insert(sigmap(wire)); + } + } + + for (auto wire : module->wires()) + { if (!wire->port_input || GetSize(wire) != 1) continue; SigBit bit = sigmap(wire); @@ -449,7 +461,7 @@ struct Smt2Worker bool is_signed = cell->getParam(ID::A_SIGNED).as_bool(); int width = GetSize(sig_y); - if (type == 's' || type == 'd' || type == 'b') { + if (type == 's' || type == 'S' || type == 'd' || type == 'b') { width = max(width, GetSize(cell->getPort(ID::A))); if (cell->hasPort(ID::B)) width = max(width, GetSize(cell->getPort(ID::B))); @@ -462,7 +474,7 @@ struct Smt2Worker if (cell->hasPort(ID::B)) { sig_b = cell->getPort(ID::B); - sig_b.extend_u0(width, is_signed && !(type == 's')); + sig_b.extend_u0(width, (type == 'S') || (is_signed && !(type == 's'))); } std::string processed_expr; @@ -577,31 +589,41 @@ struct Smt2Worker if (cell->type.in(ID($ff), ID($dff))) { registers.insert(cell); + for (auto chunk : cell->getPort(ID::Q).chunks()) + if (chunk.is_wire()) + decls.push_back(witness_signal("reg", chunk.width, chunk.offset, "", idcounter, chunk.wire)); makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort(ID::Q)), log_signal(cell->getPort(ID::Q))); register_bv(cell->getPort(ID::Q), idcounter++); recursive_cells.erase(cell); return; } - if (cell->type.in(ID($anyconst), ID($anyseq), ID($allconst), ID($allseq))) + if (cell->type.in(ID($anyconst), ID($anyseq), ID($anyinit), ID($allconst), ID($allseq))) { + auto QY = cell->type == ID($anyinit) ? ID::Q : ID::Y; registers.insert(cell); string infostr = cell->attributes.count(ID::src) ? cell->attributes.at(ID::src).decode_string().c_str() : get_id(cell); if (cell->attributes.count(ID::reg)) infostr += " " + cell->attributes.at(ID::reg).decode_string(); - decls.push_back(stringf("; yosys-smt2-%s %s#%d %d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, GetSize(cell->getPort(ID::Y)), infostr.c_str())); - if (cell->getPort(ID::Y).is_wire() && cell->getPort(ID::Y).as_wire()->get_bool_attribute(ID::maximize)){ + decls.push_back(stringf("; yosys-smt2-%s %s#%d %d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, GetSize(cell->getPort(QY)), infostr.c_str())); + if (cell->getPort(QY).is_wire() && cell->getPort(QY).as_wire()->get_bool_attribute(ID::maximize)){ decls.push_back(stringf("; yosys-smt2-maximize %s#%d\n", get_id(module), idcounter)); - log("Wire %s is maximized\n", cell->getPort(ID::Y).as_wire()->name.str().c_str()); + log("Wire %s is maximized\n", cell->getPort(QY).as_wire()->name.str().c_str()); } - else if (cell->getPort(ID::Y).is_wire() && cell->getPort(ID::Y).as_wire()->get_bool_attribute(ID::minimize)){ + else if (cell->getPort(QY).is_wire() && cell->getPort(QY).as_wire()->get_bool_attribute(ID::minimize)){ decls.push_back(stringf("; yosys-smt2-minimize %s#%d\n", get_id(module), idcounter)); - log("Wire %s is minimized\n", cell->getPort(ID::Y).as_wire()->name.str().c_str()); + log("Wire %s is minimized\n", cell->getPort(QY).as_wire()->name.str().c_str()); } - makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort(ID::Y)), log_signal(cell->getPort(ID::Y))); + + bool init_only = cell->type.in(ID($anyconst), ID($anyinit), ID($allconst)); + for (auto chunk : cell->getPort(QY).chunks()) + if (chunk.is_wire()) + decls.push_back(witness_signal(init_only ? "init" : "seq", chunk.width, chunk.offset, "", idcounter, chunk.wire)); + + makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort(QY)), log_signal(cell->getPort(QY))); if (cell->type == ID($anyseq)) ex_input_eq.push_back(stringf(" (= (|%s#%d| state) (|%s#%d| other_state))", get_id(module), idcounter, get_id(module), idcounter)); - register_bv(cell->getPort(ID::Y), idcounter++); + register_bv(cell->getPort(QY), idcounter++); recursive_cells.erase(cell); return; } @@ -619,8 +641,8 @@ struct Smt2Worker if (cell->type.in(ID($shift), ID($shiftx))) { if (cell->getParam(ID::B_SIGNED).as_bool()) { return export_bvop(cell, stringf("(ite (bvsge P #b%0*d) " - "(bvlshr A B) (bvlshr A (bvneg B)))", - GetSize(cell->getPort(ID::B)), 0), 's'); + "(bvlshr A B) (bvshl A (bvneg B)))", + GetSize(cell->getPort(ID::B)), 0), 'S'); // type 'S' sign extends B } else { return export_bvop(cell, "(bvlshr A B)", 's'); } @@ -748,6 +770,7 @@ struct Smt2Worker log_error("Memory %s.%s has mixed clocked/nonclocked write ports. This is not supported by \"write_smt2\".\n", log_id(cell), log_id(module)); decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d %s\n", get_id(mem->memid), abits, mem->width, GetSize(mem->rd_ports), GetSize(mem->wr_ports), has_async_wr ? "async" : "sync")); + decls.push_back(witness_memory(get_id(mem->memid), cell, mem)); string memstate; if (has_async_wr) { @@ -840,6 +863,7 @@ struct Smt2Worker if (m != nullptr) { decls.push_back(stringf("; yosys-smt2-cell %s %s\n", get_id(cell->type), get_id(cell->name))); + decls.push_back(witness_cell(get_id(cell->name), cell)); string cell_state = stringf("(|%s_h %s| state)", get_id(module), get_id(cell->name)); for (auto &conn : cell->connections()) @@ -920,7 +944,7 @@ struct Smt2Worker pool<SigBit> reg_bits; for (auto cell : module->cells()) - if (cell->type.in(ID($ff), ID($dff), ID($_FF_), ID($_DFF_P_), ID($_DFF_N_))) { + if (cell->type.in(ID($ff), ID($dff), ID($_FF_), ID($_DFF_P_), ID($_DFF_N_), ID($anyinit))) { // not using sigmap -- we want the net directly at the dff output for (auto bit : cell->getPort(ID::Q)) reg_bits.insert(bit); @@ -938,14 +962,19 @@ struct Smt2Worker for (auto wire : module->wires()) { bool is_register = false; - for (auto bit : SigSpec(wire)) + bool contains_clock = false; + for (auto bit : SigSpec(wire)) { if (reg_bits.count(bit)) is_register = true; + auto sig_bit = sigmap(bit); + if (clock_posedge.count(sig_bit) || clock_negedge.count(sig_bit)) + contains_clock = true; + } bool is_smtlib2_comb_expr = wire->has_attribute(ID::smtlib2_comb_expr); if (is_smtlib2_comb_expr && !is_smtlib2_module) log_error("smtlib2_comb_expr is only valid in a module with the smtlib2_module attribute: wire %s.%s", log_id(module), log_id(wire)); - if (wire->port_id || is_register || wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name.isPublic())) { + if (wire->port_id || is_register || contains_clock || wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name.isPublic())) { RTLIL::SigSpec sig = sigmap(wire); std::vector<std::string> comments; if (wire->port_input) @@ -956,9 +985,20 @@ struct Smt2Worker comments.push_back(stringf("; yosys-smt2-register %s %d\n", get_id(wire), wire->width)); if (wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name.isPublic())) comments.push_back(stringf("; yosys-smt2-wire %s %d\n", get_id(wire), wire->width)); - if (GetSize(wire) == 1 && (clock_posedge.count(sig) || clock_negedge.count(sig))) + if (contains_clock && GetSize(wire) == 1 && (clock_posedge.count(sig) || clock_negedge.count(sig))) comments.push_back(stringf("; yosys-smt2-clock %s%s%s\n", get_id(wire), clock_posedge.count(sig) ? " posedge" : "", clock_negedge.count(sig) ? " negedge" : "")); + if (contains_clock) { + for (int i = 0; i < GetSize(sig); i++) { + bool is_posedge = clock_posedge.count(sig[i]); + bool is_negedge = clock_negedge.count(sig[i]); + if (is_posedge != is_negedge) + comments.push_back(witness_signal( + is_posedge ? "posedge" : "negedge", 1, i, get_id(wire), -1, wire)); + } + } + if (wire->port_input) + comments.push_back(witness_signal("input", wire->width, 0, get_id(wire), -1, wire)); std::string smtlib2_comb_expr; if (is_smtlib2_comb_expr) { smtlib2_comb_expr = @@ -968,6 +1008,8 @@ struct Smt2Worker if (!bvmode && GetSize(sig) > 1) log_error("smtlib2_comb_expr is unsupported on multi-bit wires when -nobv is specified: wire %s.%s", log_id(module), log_id(wire)); + + comments.push_back(witness_signal("blackbox", wire->width, 0, get_id(wire), -1, wire)); } auto &out_decls = is_smtlib2_comb_expr ? smtlib2_decls : decls; if (bvmode && GetSize(sig) > 1) { @@ -1136,7 +1178,7 @@ struct Smt2Worker ex_state_eq.push_back(stringf("(= %s %s)", get_bool(cell->getPort(ID::Q)).c_str(), get_bool(cell->getPort(ID::Q), "other_state").c_str())); } - if (cell->type.in(ID($ff), ID($dff))) + if (cell->type.in(ID($ff), ID($dff), ID($anyinit))) { std::string expr_d = get_bv(cell->getPort(ID::D)); std::string expr_q = get_bv(cell->getPort(ID::Q), "next_state"); @@ -1435,6 +1477,90 @@ struct Smt2Worker f << "true)"; f << stringf(" ; end of module %s\n", get_id(module)); } + + template<class T> static std::vector<std::string> witness_path(T *obj) { + std::vector<std::string> path; + if (obj->name.isPublic()) { + auto hdlname = obj->get_string_attribute(ID::hdlname); + for (auto token : split_tokens(hdlname)) + path.push_back("\\" + token); + } + if (path.empty()) + path.push_back(obj->name.str()); + return path; + } + + std::string witness_signal(const char *type, int width, int offset, const std::string &smtname, int smtid, RTLIL::Wire *wire) + { + std::vector<std::string> hiername; + const char *wire_name = wire->name.c_str(); + if (wire_name[0] == '\\') { + auto hdlname = wire->get_string_attribute(ID::hdlname); + for (auto token : split_tokens(hdlname)) + hiername.push_back("\\" + token); + } + if (hiername.empty()) + hiername.push_back(wire->name.str()); + + std::string line = "; yosys-smt2-witness "; + (json11::Json { json11::Json::object { + { "type", type }, + { "offset", offset }, + { "width", width }, + { "smtname", smtname.empty() ? json11::Json(smtid) : json11::Json(smtname) }, + { "path", witness_path(wire) }, + }}).dump(line); + line += "\n"; + return line; + } + + std::string witness_cell(const char *smtname, RTLIL::Cell *cell) + { + std::string line = "; yosys-smt2-witness "; + (json11::Json {json11::Json::object { + { "type", "cell" }, + { "smtname", smtname }, + { "path", witness_path(cell) }, + }}).dump(line); + line += "\n"; + return line; + } + + std::string witness_memory(const char *smtname, RTLIL::Cell *cell, Mem *mem) + { + json11::Json::array uninitialized; + auto init_data = mem->get_init_data(); + + int cursor = 0; + + while (cursor < init_data.size()) { + while (cursor < init_data.size() && init_data[cursor] != State::Sx) + cursor++; + int offset = cursor; + while (cursor < init_data.size() && init_data[cursor] == State::Sx) + cursor++; + int width = cursor - offset; + if (width) + uninitialized.push_back(json11::Json::object { + {"width", width}, + {"offset", offset}, + }); + } + + std::string line = "; yosys-smt2-witness "; + (json11::Json { json11::Json::object { + { "type", "mem" }, + { "width", mem->width }, + { "size", mem->size }, + { "rom", mem->wr_ports.empty() }, + { "statebv", statebv }, + { "smtname", smtname }, + { "uninitialized", uninitialized }, + { "path", witness_path(cell) }, + }}).dump(line); + line += "\n"; + return line; + } }; struct Smt2Backend : public Backend { diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py index 137182f33..5f05287de 100644 --- a/backends/smt2/smtbmc.py +++ b/backends/smt2/smtbmc.py @@ -17,9 +17,10 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -import os, sys, getopt, re +import os, sys, getopt, re, bisect ##yosys-sys-path## from smtio import SmtIo, SmtOpts, MkVcd +from ywio import ReadWitness, WriteWitness, WitnessValues from collections import defaultdict got_topt = False @@ -28,6 +29,8 @@ step_size = 1 num_steps = 20 append_steps = 0 vcdfile = None +inywfile = None +outywfile = None cexfile = None aimfile = None aiwfile = None @@ -51,6 +54,7 @@ smtctop = None noinit = False binarymode = False keep_going = False +check_witness = False so = SmtOpts() @@ -94,6 +98,9 @@ def usage(): the AIGER witness file does not include the status and properties lines. + --yw <yosys_witness_filename> + read a Yosys witness. + --btorwit <btor_witness_filename> read a BTOR witness. @@ -121,6 +128,9 @@ def usage(): (hint: use 'write_smt2 -wires' for maximum coverage of signals in generated VCD file) + --dump-yw <yw_filename> + write trace as a Yosys witness trace + --dump-vlogtb <verilog_filename> write trace as Verilog test bench @@ -161,15 +171,19 @@ def usage(): covering all found failed assertions, the character '%' is replaced in all dump filenames with an increasing number. + --check-witness + check that the used witness file contains sufficient + constraints to force an assertion failure. + """ + so.helpmsg()) sys.exit(1) try: opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:igcm:", so.longopts + - ["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "btorwit=", "presat", - "dump-vcd=", "dump-vlogtb=", "vlogtb-top=", "dump-smtc=", "dump-all", "noinfo", "append=", - "smtc-init", "smtc-top=", "noinit", "binary", "keep-going"]) + ["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "yw=", "btorwit=", "presat", + "dump-vcd=", "dump-yw=", "dump-vlogtb=", "vlogtb-top=", "dump-smtc=", "dump-all", "noinfo", "append=", + "smtc-init", "smtc-top=", "noinit", "binary", "keep-going", "check-witness"]) except: usage() @@ -204,10 +218,14 @@ for o, a in opts: aiwfile = a + ".aiw" elif o == "--aig-noheader": aigheader = False + elif o == "--yw": + inywfile = a elif o == "--btorwit": btorwitfile = a elif o == "--dump-vcd": vcdfile = a + elif o == "--dump-yw": + outywfile = a elif o == "--dump-vlogtb": vlogtbfile = a elif o == "--vlogtb-top": @@ -244,6 +262,8 @@ for o, a in opts: binarymode = True elif o == "--keep-going": keep_going = True + elif o == "--check-witness": + check_witness = True elif so.handle(o, a): pass else: @@ -462,7 +482,8 @@ if cexfile is not None: constr_assumes[step].append((cexfile, smtexpr)) if not got_topt: - skip_steps = max(skip_steps, step) + if not check_witness: + skip_steps = max(skip_steps, step) num_steps = max(num_steps, step+1) if aimfile is not None: @@ -595,13 +616,119 @@ if aimfile is not None: constr_assumes[step].append((cexfile, smtexpr)) if not got_topt: - skip_steps = max(skip_steps, step) + if not check_witness: + skip_steps = max(skip_steps, step) # some solvers optimize the properties so that they fail one cycle early, # thus we check the properties in the cycle the aiger witness ends, and # if that doesn't work, we check the cycle after that as well. num_steps = max(num_steps, step+2) step += 1 +if inywfile is not None: + if not got_topt: + assume_skipped = 0 + skip_steps = 0 + num_steps = 0 + + with open(inywfile, "r") as f: + inyw = ReadWitness(f) + + inits, seqs, clocks, mems = smt.hierwitness(topmod, allregs=True, blackbox=True) + + smt_wires = defaultdict(list) + smt_mems = defaultdict(list) + + for wire in inits + seqs: + smt_wires[wire["path"]].append(wire) + + for mem in mems: + smt_mems[mem["path"]].append(mem) + + addr_re = re.compile(r'\\\[[0-9]+\]$') + bits_re = re.compile(r'[01?]*$') + + for t, step in inyw.steps(): + present_signals, missing = step.present_signals(inyw.sigmap) + for sig in present_signals: + bits = step[sig] + if not bits_re.match(bits): + raise ValueError("unsupported bit value in Yosys witness file") + + sig_end = sig.offset + len(bits) + if sig.path in smt_wires: + for wire in smt_wires[sig.path]: + width, offset = wire["width"], wire["offset"] + + smt_bool = smt.net_width(topmod, wire["smtpath"]) == 1 + + offset = max(offset, 0) + + end = width + offset + common_offset = max(sig.offset, offset) + common_end = min(sig_end, end) + if common_end <= common_offset: + continue + + smt_expr = smt.net_expr(topmod, f"s{t}", wire["smtpath"]) + + if not smt_bool: + slice_high = common_end - offset - 1 + slice_low = common_offset - offset + smt_expr = "((_ extract %d %d) %s)" % (slice_high, slice_low, smt_expr) + + bit_slice = bits[len(bits) - (common_end - sig.offset):len(bits) - (common_offset - sig.offset)] + + if bit_slice.count("?") == len(bit_slice): + continue + + if smt_bool: + assert width == 1 + smt_constr = "(= %s %s)" % (smt_expr, "true" if bit_slice == "1" else "false") + else: + if "?" in bit_slice: + mask = bit_slice.replace("0", "1").replace("?", "0") + bit_slice = bit_slice.replace("?", "0") + smt_expr = "(bvand %s #b%s)" % (smt_expr, mask) + + smt_constr = "(= %s #b%s)" % (smt_expr, bit_slice) + + constr_assumes[t].append((inywfile, smt_constr)) + + if sig.memory_path: + if sig.memory_path in smt_mems: + for mem in smt_mems[sig.memory_path]: + width, size, bv = mem["width"], mem["size"], mem["statebv"] + + smt_expr = smt.net_expr(topmod, f"s{t}", mem["smtpath"]) + + if bv: + word_low = sig.memory_addr * width + word_high = word_low + width - 1 + smt_expr = "((_ extract %d %d) %s)" % (word_high, word_low, smt_expr) + else: + addr_width = (size - 1).bit_length() + addr_bits = f"{sig.memory_addr:0{addr_width}b}" + smt_expr = "(select %s #b%s )" % (smt_expr, addr_bits) + + if len(bits) < width: + slice_high = sig.offset + len(bits) - 1 + smt_expr = "((_ extract %d %d) %s)" % (slice_high, sig.offset, smt_expr) + + bit_slice = bits + + if "?" in bit_slice: + mask = bit_slice.replace("0", "1").replace("?", "0") + bit_slice = bit_slice.replace("?", "0") + smt_expr = "(bvand %s #b%s)" % (smt_expr, mask) + + smt_constr = "(= %s #b%s)" % (smt_expr, bit_slice) + constr_assumes[t].append((inywfile, smt_constr)) + + if not got_topt: + if not check_witness: + skip_steps = max(skip_steps, t) + num_steps = max(num_steps, t+1) + if btorwitfile is not None: with open(btorwitfile, "r") as f: step = None @@ -699,128 +826,137 @@ if btorwitfile is not None: skip_steps = step num_steps = step+1 -def write_vcd_trace(steps_start, steps_stop, index): - filename = vcdfile.replace("%", index) - print_msg("Writing trace to VCD file: %s" % (filename)) +def collect_mem_trace_data(steps_start, steps_stop, vcd=None): + mem_trace_data = dict() - with open(filename, "w") as vcd_file: - vcd = MkVcd(vcd_file) - path_list = list() + for mempath in sorted(smt.hiermems(topmod)): + abits, width, rports, wports, asyncwr = smt.mem_info(topmod, mempath) - for netpath in sorted(smt.hiernets(topmod)): - hidden_net = False - for n in netpath: - if n.startswith("$"): - hidden_net = True - if not hidden_net: - edge = smt.net_clock(topmod, netpath) - if edge is None: - vcd.add_net([topmod] + netpath, smt.net_width(topmod, netpath)) - else: - vcd.add_clock([topmod] + netpath, edge) - path_list.append(netpath) - - mem_trace_data = dict() - for mempath in sorted(smt.hiermems(topmod)): - abits, width, rports, wports, asyncwr = smt.mem_info(topmod, mempath) + expr_id = list() + expr_list = list() + for i in range(steps_start, steps_stop): + for j in range(rports): + expr_id.append(('R', i-steps_start, j, 'A')) + expr_id.append(('R', i-steps_start, j, 'D')) + expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dA" % j)) + expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dD" % j)) + for j in range(wports): + expr_id.append(('W', i-steps_start, j, 'A')) + expr_id.append(('W', i-steps_start, j, 'D')) + expr_id.append(('W', i-steps_start, j, 'M')) + expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dA" % j)) + expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dD" % j)) + expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dM" % j)) + + rdata = list() + wdata = list() + addrs = set() + + for eid, edat in zip(expr_id, smt.get_list(expr_list)): + t, i, j, f = eid + + if t == 'R': + c = rdata + elif t == 'W': + c = wdata + else: + assert False - expr_id = list() - expr_list = list() - for i in range(steps_start, steps_stop): - for j in range(rports): - expr_id.append(('R', i-steps_start, j, 'A')) - expr_id.append(('R', i-steps_start, j, 'D')) - expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dA" % j)) - expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dD" % j)) - for j in range(wports): - expr_id.append(('W', i-steps_start, j, 'A')) - expr_id.append(('W', i-steps_start, j, 'D')) - expr_id.append(('W', i-steps_start, j, 'M')) - expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dA" % j)) - expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dD" % j)) - expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dM" % j)) - - rdata = list() - wdata = list() - addrs = set() - - for eid, edat in zip(expr_id, smt.get_list(expr_list)): - t, i, j, f = eid - - if t == 'R': - c = rdata - elif t == 'W': - c = wdata - else: - assert False + while len(c) <= i: + c.append(list()) + c = c[i] - while len(c) <= i: - c.append(list()) - c = c[i] + while len(c) <= j: + c.append(dict()) + c = c[j] - while len(c) <= j: - c.append(dict()) - c = c[j] + c[f] = smt.bv2bin(edat) - c[f] = smt.bv2bin(edat) + if f == 'A': + addrs.add(c[f]) - if f == 'A': - addrs.add(c[f]) + for addr in addrs: + tdata = list() + data = ["x"] * width + gotread = False - for addr in addrs: - tdata = list() - data = ["x"] * width - gotread = False + if len(wdata) == 0 and len(rdata) != 0: + wdata = [[]] * len(rdata) - if len(wdata) == 0 and len(rdata) != 0: - wdata = [[]] * len(rdata) + assert len(rdata) == len(wdata) - assert len(rdata) == len(wdata) + for i in range(len(wdata)): + if not gotread: + for j_data in rdata[i]: + if j_data["A"] == addr: + data = list(j_data["D"]) + gotread = True + break - for i in range(len(wdata)): - if not gotread: - for j_data in rdata[i]: - if j_data["A"] == addr: - data = list(j_data["D"]) - gotread = True - break + if gotread: + buf = data[:] + for ii in reversed(range(len(tdata))): + for k in range(width): + if tdata[ii][k] == "x": + tdata[ii][k] = buf[k] + else: + buf[k] = tdata[ii][k] - if gotread: - buf = data[:] - for ii in reversed(range(len(tdata))): - for k in range(width): - if tdata[ii][k] == "x": - tdata[ii][k] = buf[k] - else: - buf[k] = tdata[ii][k] + if not asyncwr: + tdata.append(data[:]) - if not asyncwr: - tdata.append(data[:]) + for j_data in wdata[i]: + if j_data["A"] != addr: + continue - for j_data in wdata[i]: - if j_data["A"] != addr: - continue + D = j_data["D"] + M = j_data["M"] - D = j_data["D"] - M = j_data["M"] + for k in range(width): + if M[k] == "1": + data[k] = D[k] - for k in range(width): - if M[k] == "1": - data[k] = D[k] + if asyncwr: + tdata.append(data[:]) - if asyncwr: - tdata.append(data[:]) + assert len(tdata) == len(rdata) - assert len(tdata) == len(rdata) + int_addr = int(addr, 2) - netpath = mempath[:] - netpath[-1] += "<%0*x>" % ((len(addr)+3) // 4, int(addr, 2)) + netpath = mempath[:] + if vcd: + netpath[-1] += "<%0*x>" % ((len(addr)+3) // 4, int_addr) vcd.add_net([topmod] + netpath, width) - for i in range(steps_start, steps_stop): - if i not in mem_trace_data: - mem_trace_data[i] = list() - mem_trace_data[i].append((netpath, "".join(tdata[i-steps_start]))) + for i in range(steps_start, steps_stop): + if i not in mem_trace_data: + mem_trace_data[i] = list() + mem_trace_data[i].append((netpath, int_addr, "".join(tdata[i-steps_start]))) + + return mem_trace_data + +def write_vcd_trace(steps_start, steps_stop, index): + filename = vcdfile.replace("%", index) + print_msg("Writing trace to VCD file: %s" % (filename)) + + with open(filename, "w") as vcd_file: + vcd = MkVcd(vcd_file) + path_list = list() + + for netpath in sorted(smt.hiernets(topmod)): + hidden_net = False + for n in netpath: + if n.startswith("$"): + hidden_net = True + if not hidden_net: + edge = smt.net_clock(topmod, netpath) + if edge is None: + vcd.add_net([topmod] + netpath, smt.net_width(topmod, netpath)) + else: + vcd.add_clock([topmod] + netpath, edge) + path_list.append(netpath) + + mem_trace_data = collect_mem_trace_data(steps_start, steps_stop, vcd) for i in range(steps_start, steps_stop): vcd.set_time(i) @@ -828,7 +964,7 @@ def write_vcd_trace(steps_start, steps_stop, index): for path, value in zip(path_list, value_list): vcd.set_net([topmod] + path, value) if i in mem_trace_data: - for path, value in mem_trace_data[i]: + for path, addr, value in mem_trace_data[i]: vcd.set_net([topmod] + path, value) vcd.set_time(steps_stop) @@ -1072,8 +1208,72 @@ def write_constr_trace(steps_start, steps_stop, index): for name, val in zip(pi_names, pi_values): print("assume (= [%s%s] %s)" % (constr_prefix, ".".join(name), val), file=f) +def write_yw_trace(steps_start, steps_stop, index, allregs=False): + filename = outywfile.replace("%", index) + print_msg("Writing trace to Yosys witness file: %s" % (filename)) + + mem_trace_data = collect_mem_trace_data(steps_start, steps_stop) + + with open(filename, "w") as f: + inits, seqs, clocks, mems = smt.hierwitness(topmod, allregs) + + yw = WriteWitness(f, "smtbmc") + + for clock in clocks: + yw.add_clock(clock["path"], clock["offset"], clock["type"]) + + for seq in seqs: + seq["sig"] = yw.add_sig(seq["path"], seq["offset"], seq["width"]) -def write_trace(steps_start, steps_stop, index): + for init in inits: + init["sig"] = yw.add_sig(init["path"], init["offset"], init["width"], True) + + inits = seqs + inits + + mem_dict = {tuple(mem["smtpath"]): mem for mem in mems} + mem_init_values = [] + + for path, addr, value in mem_trace_data.get(0, ()): + json_mem = mem_dict.get(tuple(path)) + if not json_mem: + continue + + bit_addr = addr * json_mem["width"] + uninit_chunks = [(chunk["width"] + chunk["offset"], chunk["offset"]) for chunk in json_mem["uninitialized"]] + first_chunk_nr = bisect.bisect_left(uninit_chunks, (bit_addr + 1,)) + + for uninit_end, uninit_offset in uninit_chunks[first_chunk_nr:]: + assert uninit_end > bit_addr + if uninit_offset > bit_addr + json_mem["width"]: + break + + word_path = (*json_mem["path"], f"\\[{addr}]") + + overlap_start = max(uninit_offset - bit_addr, 0) + overlap_end = min(uninit_end - bit_addr, json_mem["width"]) + overlap_bits = value[len(value)-overlap_end:len(value)-overlap_start] + + sig = yw.add_sig(word_path, overlap_start, overlap_end - overlap_start, True) + mem_init_values.append((sig, overlap_bits.replace("x", "?"))) + + for k in range(steps_start, steps_stop): + step_values = WitnessValues() + + if k == steps_start: + for sig, value in mem_init_values: + step_values[sig] = value + sigs = inits + seqs + else: + sigs = seqs + + for sig in sigs: + step_values[sig["sig"]] = smt.bv2bin(smt.get(smt.net_expr(topmod, f"s{k}", sig["smtpath"]))) + yw.step(step_values) + + yw.end_trace() + + +def write_trace(steps_start, steps_stop, index, allregs=False): if vcdfile is not None: write_vcd_trace(steps_start, steps_stop, index) @@ -1083,6 +1283,9 @@ def write_trace(steps_start, steps_stop, index): if outconstr is not None: write_constr_trace(steps_start, steps_stop, index) + if outywfile is not None: + write_yw_trace(steps_start, steps_stop, index, allregs) + def print_failed_asserts_worker(mod, state, path, extrainfo, infomap, infokey=()): assert mod in smt.modinfo @@ -1392,12 +1595,12 @@ if tempind: print_msg("Temporal induction failed!") print_anyconsts(num_steps) print_failed_asserts(num_steps) - write_trace(step, num_steps+1, '%') + write_trace(step, num_steps+1, '%', allregs=True) elif dumpall: print_anyconsts(num_steps) print_failed_asserts(num_steps) - write_trace(step, num_steps+1, "%d" % step) + write_trace(step, num_steps+1, "%d" % step, allregs=True) else: print_msg("Temporal induction successful.") @@ -1590,6 +1793,7 @@ else: # not tempind, covermode smt_assert("(not %s)" % active_assert_expr) else: + active_assert_expr = "true" smt_assert("false") @@ -1597,6 +1801,17 @@ else: # not tempind, covermode if retstatus != "FAILED": print("%s BMC failed!" % smt.timestamp()) + if check_witness: + print_msg("Checking witness constraints...") + smt_pop() + smt_push() + smt_assert(active_assert_expr) + if smt_check_sat() != "sat": + retstatus = "PASSED" + check_witness = False + num_steps = -1 + break + if append_steps > 0: for i in range(last_check_step+1, last_check_step+1+append_steps): print_msg("Appending additional step %d." % i) @@ -1689,6 +1904,8 @@ else: # not tempind, covermode print_anyconsts(0) write_trace(0, num_steps, '%') + if check_witness: + retstatus = "FAILED" smt.write("(exit)") smt.wait() diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py index 91efc13a3..de09c040e 100644 --- a/backends/smt2/smtio.py +++ b/backends/smt2/smtio.py @@ -16,7 +16,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -import sys, re, os, signal +import sys, re, os, signal, json import subprocess if os.name == "posix": import resource @@ -108,6 +108,7 @@ class SmtModInfo: self.allconsts = dict() self.allseqs = dict() self.asize = dict() + self.witness = [] class SmtIo: @@ -337,7 +338,7 @@ class SmtIo: def p_thread_main(self): while True: - data = self.p.stdout.readline().decode("ascii") + data = self.p.stdout.readline().decode("utf-8") if data == "": break self.p_queue.put(data) self.p_queue.put("") @@ -359,7 +360,7 @@ class SmtIo: def p_write(self, data, flush): assert self.p is not None - self.p.stdin.write(bytes(data, "ascii")) + self.p.stdin.write(bytes(data, "utf-8")) if flush: self.p.stdin.flush() def p_read(self): @@ -587,6 +588,11 @@ class SmtIo: self.modinfo[self.curmod].allseqs[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5]) self.modinfo[self.curmod].asize[fields[2]] = int(fields[3]) + if fields[1] == "yosys-smt2-witness": + data = json.loads(stmt.split(None, 2)[2]) + if data.get("type") in ["cell", "mem", "posedge", "negedge", "input", "reg", "init", "seq", "blackbox"]: + self.modinfo[self.curmod].witness.append(data) + def hiernets(self, top, regs_only=False): def hiernets_worker(nets, mod, cursor): for netname in sorted(self.modinfo[mod].wsize.keys()): @@ -658,6 +664,57 @@ class SmtIo: hiermems_worker(mems, top, []) return mems + def hierwitness(self, top, allregs=False, blackbox=True): + init_witnesses = [] + seq_witnesses = [] + clk_witnesses = [] + mem_witnesses = [] + + def absolute(path, cursor, witness): + return { + **witness, + "path": path + tuple(witness["path"]), + "smtpath": cursor + [witness["smtname"]], + } + + for witness in self.modinfo[top].witness: + if witness["type"] == "input": + seq_witnesses.append(absolute((), [], witness)) + if witness["type"] in ("posedge", "negedge"): + clk_witnesses.append(absolute((), [], witness)) + + init_types = ["init"] + if allregs: + init_types.append("reg") + + seq_types = ["seq"] + if blackbox: + seq_types.append("blackbox") + + def worker(mod, path, cursor): + cell_paths = {} + for witness in self.modinfo[mod].witness: + if witness["type"] in init_types: + init_witnesses.append(absolute(path, cursor, witness)) + if witness["type"] in seq_types: + seq_witnesses.append(absolute(path, cursor, witness)) + if witness["type"] == "mem": + if allregs and not witness["rom"]: + width, size = witness["width"], witness["size"] + witness = {**witness, "uninitialized": {"width": width * size, "offset": 0}} + if not witness["uninitialized"]: + continue + + mem_witnesses.append(absolute(path, cursor, witness)) + if witness["type"] == "cell": + cell_paths[witness["smtname"]] = tuple(witness["path"]) + + for cellname, celltype in sorted(self.modinfo[mod].cells.items()): + worker(celltype, path + cell_paths.get(cellname, ("?" + cellname,)), cursor + [cellname]) + + worker(top, (), []) + return init_witnesses, seq_witnesses, clk_witnesses, mem_witnesses + def read(self): stmt = [] count_brackets = 0 @@ -887,6 +944,8 @@ class SmtIo: assert mod in self.modinfo if path[0] == "": return base + if isinstance(path[0], int): + return "(|%s#%d| %s)" % (mod, path[0], base) if path[0] in self.modinfo[mod].cells: return "(|%s_h %s| %s)" % (mod, path[0], base) if path[0] in self.modinfo[mod].wsize: @@ -909,6 +968,8 @@ class SmtIo: mod = self.modinfo[mod].cells[net_path[i]] assert mod in self.modinfo + if isinstance(net_path[-1], int): + return None assert net_path[-1] in self.modinfo[mod].wsize return self.modinfo[mod].wsize[net_path[-1]] diff --git a/backends/smt2/witness.py b/backends/smt2/witness.py new file mode 100644 index 000000000..a1e65569d --- /dev/null +++ b/backends/smt2/witness.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python3 +# +# yosys -- Yosys Open SYnthesis Suite +# +# Copyright (C) 2022 Jannis Harder <jix@yosyshq.com> <me@jix.one> +# +# 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. +# + +import os, sys, itertools, re +##yosys-sys-path## +import json +import click + +from ywio import ReadWitness, WriteWitness, WitnessSig, WitnessSigMap, WitnessValues, coalesce_signals + +@click.group() +def cli(): + pass + + +@cli.command(help=""" +Display a Yosys witness trace in a human readable format. +""") +@click.argument("input", type=click.File("r")) +def display(input): + click.echo(f"Reading Yosys witness trace {input.name!r}...") + inyw = ReadWitness(input) + + def output(): + + yield click.style("*** RTLIL bit-order below may differ from source level declarations ***", fg="red") + if inyw.clocks: + yield click.style("=== Clock Signals ===", fg="blue") + for clock in inyw.clocks: + yield f" {clock['edge']} {WitnessSig(clock['path'], clock['offset']).pretty()}" + + for t, values in inyw.steps(): + if t: + yield click.style(f"=== Step {t} ===", fg="blue") + else: + yield click.style("=== Initial State ===", fg="blue") + + step_prefix = click.style(f"#{t}", fg="bright_black") + + signals, missing = values.present_signals(inyw.sigmap) + + assert not missing + + for sig in signals: + display_bits = values[sig].replace("?", click.style("?", fg="bright_black")) + yield f" {step_prefix} {sig.pretty()} = {display_bits}" + click.echo_via_pager([line + "\n" for line in output()]) + + +@cli.command(help=""" +Display statistics of a Yosys witness trace. +""") +@click.argument("input", type=click.File("r")) +def stats(input): + click.echo(f"Reading Yosys witness trace {input.name!r}...") + inyw = ReadWitness(input) + + total = 0 + + for t, values in inyw.steps(): + click.echo(f"{t:5}: {len(values.values):8} bits") + total += len(values.values) + + click.echo(f"total: {total:8} bits") + + +@cli.command(help=""" +Transform a Yosys witness trace. + +Currently no transformations are implemented, so it is only useful for testing. +""") +@click.argument("input", type=click.File("r")) +@click.argument("output", type=click.File("w")) +def yw2yw(input, output): + click.echo(f"Copying yosys witness trace from {input.name!r} to {output.name!r}...") + inyw = ReadWitness(input) + outyw = WriteWitness(output, "yosys-witness yw2yw") + + for clock in inyw.clocks: + outyw.add_clock(clock["path"], clock["offset"], clock["edge"]) + + for sig in inyw.signals: + outyw.add_sig(sig.path, sig.offset, sig.width, sig.init_only) + + for t, values in inyw.steps(): + outyw.step(values) + + outyw.end_trace() + + click.echo(f"Copied {outyw.t + 1} time steps.") + + +class AigerMap: + def __init__(self, mapfile): + data = json.load(mapfile) + + self.latch_count = data["latch_count"] + self.input_count = data["input_count"] + + self.clocks = data["clocks"] + + self.sigmap = WitnessSigMap() + self.init_inputs = set(init["input"] for init in data["inits"]) + + for bit in data["inputs"] + data["seqs"] + data["inits"]: + self.sigmap.add_bit((tuple(bit["path"]), bit["offset"]), bit["input"]) + + + +@cli.command(help=""" +Convert an AIGER witness trace into a Yosys witness trace. + +This requires a Yosys witness AIGER map file as generated by 'write_aiger -ywmap'. +""") +@click.argument("input", type=click.File("r")) +@click.argument("mapfile", type=click.File("r")) +@click.argument("output", type=click.File("w")) +def aiw2yw(input, mapfile, output): + input_name = input.name + click.echo(f"Converting AIGER witness trace {input_name!r} to Yosys witness trace {output.name!r}...") + click.echo(f"Using Yosys witness AIGER map file {mapfile.name!r}") + aiger_map = AigerMap(mapfile) + + header_lines = list(itertools.islice(input, 0, 2)) + + if len(header_lines) == 2 and header_lines[1][0] in ".bcjf": + status = header_lines[0].strip() + if status == "0": + raise click.ClickException(f"{input_name}: file contains no trace, the AIGER status is unsat") + elif status == "2": + raise click.ClickException(f"{input_name}: file contains no trace, the AIGER status is sat") + elif status != "1": + raise click.ClickException(f"{input_name}: unexpected data in AIGER witness file") + else: + input = itertools.chain(header_lines, input) + + ffline = next(input, None) + if ffline is None: + raise click.ClickException(f"{input_name}: unexpected end of file") + ffline = ffline.strip() + if not re.match(r'[01x]*$', ffline): + raise click.ClickException(f"{input_name}: unexpected data in AIGER witness file") + if not re.match(r'[0]*$', ffline): + raise click.ClickException(f"{input_name}: non-default initial state not supported") + + outyw = WriteWitness(output, 'yosys-witness aiw2yw') + + for clock in aiger_map.clocks: + outyw.add_clock(clock["path"], clock["offset"], clock["edge"]) + + for (path, offset), id in aiger_map.sigmap.bit_to_id.items(): + outyw.add_sig(path, offset, init_only=id in aiger_map.init_inputs) + + missing = set() + + while True: + inline = next(input, None) + if inline is None: + click.echo(f"Warning: {input_name}: file may be incomplete") + break + inline = inline.strip() + if inline in [".", "# DONE"]: + break + if inline.startswith("#"): + continue + + if not re.match(r'[01x]*$', ffline): + raise click.ClickException(f"{input_name}: unexpected data in AIGER witness file") + + if len(inline) != aiger_map.input_count: + raise click.ClickException( + f"{input_name}: {mapfile.name}: number of inputs does not match, " + f"{len(inline)} in witness, {aiger_map.input_count} in map file") + + values = WitnessValues() + for i, v in enumerate(inline): + if v == "x" or outyw.t > 0 and i in aiger_map.init_inputs: + continue + + try: + bit = aiger_map.sigmap.id_to_bit[i] + except IndexError: + bit = None + if bit is None: + missing.insert(i) + + values[bit] = v + + outyw.step(values) + + outyw.end_trace() + + if missing: + click.echo("The following AIGER inputs belong to unknown signals:") + click.echo(" " + " ".join(str(id) for id in sorted(missing))) + + click.echo(f"Converted {outyw.t} time steps.") + +@cli.command(help=""" +Convert a Yosys witness trace into an AIGER witness trace. + +This requires a Yosys witness AIGER map file as generated by 'write_aiger -ywmap'. +""") +@click.argument("input", type=click.File("r")) +@click.argument("mapfile", type=click.File("r")) +@click.argument("output", type=click.File("w")) +def yw2aiw(input, mapfile, output): + click.echo(f"Converting Yosys witness trace {input.name!r} to AIGER witness trace {output.name!r}...") + click.echo(f"Using Yosys witness AIGER map file {mapfile.name!r}") + aiger_map = AigerMap(mapfile) + inyw = ReadWitness(input) + + print("1", file=output) + print("b0", file=output) + # TODO the b0 status isn't really accurate, but we don't have any better info here + print("0" * aiger_map.latch_count, file=output) + + all_missing = set() + + for t, step in inyw.steps(): + bits, missing = step.pack_present(aiger_map.sigmap) + bits = bits[::-1].replace('?', 'x') + all_missing.update(missing) + bits += 'x' * (aiger_map.input_count - len(bits)) + print(bits, file=output) + + print(".", file=output) + + if all_missing: + click.echo("The following signals are missing in the AIGER map file and will be dropped:") + for sig in coalesce_signals(WitnessSig(*bit) for bit in all_missing): + click.echo(" " + sig.pretty()) + + + click.echo(f"Converted {len(inyw)} time steps.") + +if __name__ == "__main__": + cli() diff --git a/backends/smt2/ywio.py b/backends/smt2/ywio.py new file mode 100644 index 000000000..8469b4162 --- /dev/null +++ b/backends/smt2/ywio.py @@ -0,0 +1,392 @@ +# +# yosys -- Yosys Open SYnthesis Suite +# +# Copyright (C) 2022 Jannis Harder <jix@yosyshq.com> <me@jix.one> +# +# 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. +# + +import json, re + +from functools import total_ordering + + +class PrettyJson: + def __init__(self, f): + self.f = f + self.indent = 0 + self.state = ["value"] + + def line(self): + indent = len(self.state) - bool(self.state and self.state[-1] == "value") + print("\n", end=" " * (2 * indent), file=self.f) + + def raw(self, str): + print(end=str, file=self.f) + + def begin_object(self): + self.begin_value() + self.raw("{") + self.state.append("object_first") + + def begin_array(self): + self.begin_value() + self.raw("[") + self.state.append("array_first") + + def end_object(self): + state = self.state.pop() + if state == "object": + self.line() + else: + assert state == "object_first" + self.raw("}") + self.end_value() + + def end_array(self): + state = self.state.pop() + if state == "array": + self.line() + else: + assert state == "array_first" + self.raw("]") + self.end_value() + + def name(self, name): + if self.state[-1] == "object_first": + self.state[-1] = "object" + else: + self.raw(",") + self.line() + json.dump(str(name), self.f) + self.raw(": ") + self.state.append("value") + + def begin_value(self): + if self.state[-1] == "array_first": + self.line() + self.state[-1] = "array" + elif self.state[-1] == "array": + self.raw(",") + self.line() + else: + assert self.state.pop() == "value" + + def end_value(self): + if not self.state: + print(file=self.f, flush=True) + + def value(self, value): + self.begin_value() + json.dump(value, self.f) + self.end_value() + + def entry(self, name, value): + self.name(name) + self.value(value) + + def object(self, entries=None): + if isinstance(entries, dict): + entries = dict.items() + self.begin_object() + for name, value in entries: + self.entry(name, value) + self.end_object() + + def array(self, values=None): + self.begin_array() + for value in values: + self.value(value) + self.end_array() + + +addr_re = re.compile(r'\\\[[0-9]+\]$') +public_name_re = re.compile(r"\\([a-zA-Z_][a-zA-Z0-9_]*(\[[0-9]+\])?|\[[0-9]+\])$") + +def pretty_name(id): + if public_name_re.match(id): + return id.lstrip("\\") + else: + return id + +def pretty_path(path): + out = "" + for name in path: + name = pretty_name(name) + if name.startswith("["): + out += name + continue + if out: + out += "." + if name.startswith("\\") or name.startswith("$"): + out += name + " " + else: + out += name + + return out + +@total_ordering +class WitnessSig: + def __init__(self, path, offset, width=1, init_only=False): + path = tuple(path) + self.path, self.width, self.offset, self.init_only = path, width, offset, init_only + + self.memory_path = None + self.memory_addr = None + + sort_path = path + sort_id = -1 + if path and addr_re.match(path[-1]): + self.memory_path = sort_path = path[:-1] + self.memory_addr = sort_id = int(path[-1][2:-1]) + + self.sort_key = (init_only, sort_path, sort_id, offset, width) + + def bits(self): + return ((self.path, i) for i in range(self.offset, self.offset + self.width)) + + def rev_bits(self): + return ((self.path, i) for i in reversed(range(self.offset, self.offset + self.width))) + + def pretty(self): + if self.width > 1: + last_offset = self.offset + self.width - 1 + return f"{pretty_path(self.path)}[{last_offset}:{self.offset}]" + else: + return f"{pretty_path(self.path)}[{self.offset}]" + + def __eq__(self): + return self.sort_key + + def __hash__(self): + return hash(self.sort_key) + + def __lt__(self, other): + return self.sort_key < other.sort_key + + +def coalesce_signals(signals): + bits = {} + for sig in signals: + for bit in sig.bits(): + if sig.init_only: + bits.setdefault(bit, False) + else: + bits[bit] = True + + active = None + + out = [] + + for bit, not_init in sorted(bits.items()): + if active: + if active[0] == bit[0] and active[2] == bit[1] and active[3] == not_init: + active[2] += 1 + else: + out.append(WitnessSig(active[0], active[1], active[2] - active[1], not active[3])) + active = None + + if active is None: + active = [bit[0], bit[1], bit[1] + 1, not_init] + + if active: + out.append(WitnessSig(active[0], active[1], active[2] - active[1], not active[3])) + + return sorted(out) + + +class WitnessSigMap: + def __init__(self, signals=[]): + self.signals = [] + + self.id_to_bit = [] + self.bit_to_id = {} + self.bit_to_sig = {} + + for sig in signals: + self.add_signal(sig) + + def add_signal(self, sig): + self.signals.append(sig) + for bit in sig.bits(): + self.add_bit(bit) + self.bit_to_sig[bit] = sig + + def add_bit(self, bit, id=None): + if id is None: + id = len(self.id_to_bit) + self.id_to_bit.append(bit) + else: + if len(self.id_to_bit) <= id: + self.id_to_bit += [None] * (id - len(self.id_to_bit) + 1) + self.id_to_bit[id] = bit + self.bit_to_id[bit] = id + + +class WitnessValues: + def __init__(self): + self.values = {} + + def __setitem__(self, key, value): + if isinstance(key, tuple) and len(key) == 2: + if value != "?": + assert isinstance(value, str) + assert len(value) == 1 + self.values[key] = value + else: + assert isinstance(key, WitnessSig) + assert key.width == len(value) + if isinstance(value, str): + value = reversed(value) + for bit, bit_value in zip(key.bits(), value): + if bit_value != "?": + self.values[bit] = bit_value + + def __getitem__(self, key): + if isinstance(key, tuple) and len(key) == 2: + return self.values.get(key, "?") + else: + assert isinstance(key, WitnessSig) + return "".join([self.values.get(bit, "?") for bit in key.rev_bits()]) + + def pack_present(self, sigmap): + missing = [] + + max_id = max((sigmap.bit_to_id.get(bit, -1) for bit in self.values), default=-1) + + vector = ["?"] * (max_id + 1) + for bit, bit_value in self.values.items(): + id = sigmap.bit_to_id.get(bit, - 1) + if id < 0: + missing.append(bit) + else: + vector[max_id - sigmap.bit_to_id[bit]] = bit_value + + return "".join(vector), missing + + def pack(self, sigmap): + packed, missing = self.pack_present(sigmap) + if missing: + raise RuntimeError(f"Cannot pack bits {missing!r}") + return packed + + def unpack(self, sigmap, bits): + for i, bit_value in enumerate(reversed(bits)): + if bit_value != "?": + self.values[sigmap.id_to_bit[i]] = bit_value + + def present_signals(self, sigmap): + signals = set(sigmap.bit_to_sig.get(bit) for bit in self.values) + missing_signals = None in signals + if missing_signals: + signals.discard(None) + + return sorted(signals), missing_signals + + +class WriteWitness: + def __init__(self, f, generator): + self.out = PrettyJson(f) + self.t = 0 + self.header_written = False + self.clocks = [] + self.signals = [] + + self.out.begin_object() + self.out.entry("format", "Yosys Witness Trace") + self.out.entry("generator", generator) + + def add_clock(self, path, offset, edge): + assert not self.header_written + self.clocks.append({ + "path": path, + "edge": edge, + "offset": offset, + }) + + def add_sig(self, path, offset, width=1, init_only=False): + assert not self.header_written + sig = WitnessSig(path, offset, width, init_only) + self.signals.append(sig) + return sig + + def write_header(self): + assert not self.header_written + self.header_written = True + self.out.name("clocks") + self.out.array(self.clocks) + + self.signals = coalesce_signals(self.signals) + self.sigmap = WitnessSigMap(self.signals) + + self.out.name("signals") + self.out.array({ + "path": sig.path, + "width": sig.width, + "offset": sig.offset, + "init_only": sig.init_only, + } for sig in self.signals) + + self.out.name("steps") + self.out.begin_array() + + def step(self, values): + if not self.header_written: + self.write_header() + + self.out.value({"bits": values.pack(self.sigmap)}) + + self.t += 1 + + def end_trace(self): + if not self.header_written: + self.write_header() + self.out.end_array() + self.out.end_object() + + +class ReadWitness: + def __init__(self, f): + data = json.load(f) + if not isinstance(data, dict): + data = {} + + data_format = data.get("format", "Unknown Format") + + if data_format != "Yosys Witness Trace": + raise ValueError(f"unsupported format {data_format!r}") + + self.clocks = data["clocks"] + for clock in self.clocks: + clock["path"] = tuple(clock["path"]) + + self.signals = [ + WitnessSig(sig["path"], sig["offset"], sig["width"], sig["init_only"]) + for sig in data["signals"] + ] + + self.sigmap = WitnessSigMap(self.signals) + + self.bits = [step["bits"] for step in data["steps"]] + + def step(self, t): + values = WitnessValues() + values.unpack(self.sigmap, self.bits[t]) + return values + + def steps(self): + for i in range(len(self.bits)): + yield i, self.step(i) + + def __len__(self): + return len(self.bits) diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index d81c53dfb..9327b34ee 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -45,7 +45,7 @@ using namespace AST_INTERNAL; // helper function for creating RTLIL code for unary operations static RTLIL::SigSpec uniop2rtlil(AstNode *that, IdString type, int result_width, const RTLIL::SigSpec &arg, bool gen_attributes = true) { - IdString name = stringf("%s$%s:%d$%d", type.c_str(), that->filename.c_str(), that->location.first_line, autoidx++); + IdString name = stringf("%s$%s:%d$%d", type.c_str(), RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); set_src_attr(cell, that); @@ -77,7 +77,7 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s return; } - IdString name = stringf("$extend$%s:%d$%d", that->filename.c_str(), that->location.first_line, autoidx++); + IdString name = stringf("$extend$%s:%d$%d", RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, ID($pos)); set_src_attr(cell, that); @@ -104,7 +104,7 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s // helper function for creating RTLIL code for binary operations static RTLIL::SigSpec binop2rtlil(AstNode *that, IdString type, int result_width, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) { - IdString name = stringf("%s$%s:%d$%d", type.c_str(), that->filename.c_str(), that->location.first_line, autoidx++); + IdString name = stringf("%s$%s:%d$%d", type.c_str(), RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); set_src_attr(cell, that); @@ -138,7 +138,7 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const log_assert(cond.size() == 1); std::stringstream sstr; - sstr << "$ternary$" << that->filename << ":" << that->location.first_line << "$" << (autoidx++); + sstr << "$ternary$" << RTLIL::encode_filename(that->filename) << ":" << that->location.first_line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($mux)); set_src_attr(cell, that); @@ -321,7 +321,7 @@ struct AST_INTERNAL::ProcessGenerator LookaheadRewriter la_rewriter(always); // generate process and simple root case - proc = current_module->addProcess(stringf("$proc$%s:%d$%d", always->filename.c_str(), always->location.first_line, autoidx++)); + proc = current_module->addProcess(stringf("$proc$%s:%d$%d", RTLIL::encode_filename(always->filename).c_str(), always->location.first_line, autoidx++)); set_src_attr(proc, always); for (auto &attr : always->attributes) { if (attr.second->type != AST_CONSTANT) @@ -1776,7 +1776,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_MEMRD: { std::stringstream sstr; - sstr << "$memrd$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + sstr << "$memrd$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($memrd)); set_src_attr(cell, this); @@ -1814,7 +1814,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_MEMINIT: { std::stringstream sstr; - sstr << "$meminit$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + sstr << "$meminit$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); SigSpec en_sig = children[2]->genRTLIL(); @@ -1869,7 +1869,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) IdString cellname; if (str.empty()) - cellname = stringf("%s$%s:%d$%d", celltype.c_str(), filename.c_str(), location.first_line, autoidx++); + cellname = stringf("%s$%s:%d$%d", celltype.c_str(), RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); else cellname = str; diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 2d9d6dc79..49bf9af09 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -1240,7 +1240,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // create the indirection wire std::stringstream sstr; - sstr << "$indirect$" << ref->name.c_str() << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + sstr << "$indirect$" << ref->name.c_str() << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); std::string tmp_str = sstr.str(); add_wire_for_ref(ref, tmp_str); @@ -2127,7 +2127,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, std::swap(data_range_left, data_range_right); std::stringstream sstr; - sstr << "$mem2bits$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + sstr << "$mem2bits$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); std::string wire_id = sstr.str(); AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(data_range_left, true), mkconst_int(data_range_right, true))); @@ -2714,14 +2714,14 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // mask and shift operations, disabled for now AstNode *wire_mask = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true))); - wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", filename.c_str(), location.first_line, autoidx++); + wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); wire_mask->attributes[ID::nosync] = AstNode::mkconst_int(1, false); wire_mask->is_logic = true; while (wire_mask->simplify(true, false, false, 1, -1, false, false)) { } current_ast_mod->children.push_back(wire_mask); AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true))); - wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", filename.c_str(), location.first_line, autoidx++); + wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); wire_data->attributes[ID::nosync] = AstNode::mkconst_int(1, false); wire_data->is_logic = true; while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } @@ -2732,7 +2732,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, shift_expr->detectSignWidth(shamt_width_hint, shamt_sign_hint); AstNode *wire_sel = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(shamt_width_hint-1, true), mkconst_int(0, true))); - wire_sel->str = stringf("$bitselwrite$sel$%s:%d$%d", filename.c_str(), location.first_line, autoidx++); + wire_sel->str = stringf("$bitselwrite$sel$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); wire_sel->attributes[ID::nosync] = AstNode::mkconst_int(1, false); wire_sel->is_logic = true; wire_sel->is_signed = shamt_sign_hint; @@ -2809,7 +2809,7 @@ skip_dynamic_range_lvalue_expansion:; if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME || type == AST_LIVE || type == AST_FAIR || type == AST_COVER) && current_block != NULL) { std::stringstream sstr; - sstr << "$formal$" << filename << ":" << location.first_line << "$" << (autoidx++); + sstr << "$formal$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); std::string id_check = sstr.str() + "_CHECK", id_en = sstr.str() + "_EN"; AstNode *wire_check = new AstNode(AST_WIRE); @@ -2918,7 +2918,7 @@ skip_dynamic_range_lvalue_expansion:; newNode = new AstNode(AST_BLOCK); AstNode *wire_tmp = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(width_hint-1, true), mkconst_int(0, true))); - wire_tmp->str = stringf("$splitcmplxassign$%s:%d$%d", filename.c_str(), location.first_line, autoidx++); + wire_tmp->str = stringf("$splitcmplxassign$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); current_ast_mod->children.push_back(wire_tmp); current_scope[wire_tmp->str] = wire_tmp; wire_tmp->attributes[ID::nosync] = AstNode::mkconst_int(1, false); @@ -2956,7 +2956,7 @@ skip_dynamic_range_lvalue_expansion:; (children[0]->children.size() == 1 || children[0]->children.size() == 2) && children[0]->children[0]->type == AST_RANGE) { std::stringstream sstr; - sstr << "$memwr$" << children[0]->str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + sstr << "$memwr$" << children[0]->str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN"; int mem_width, mem_size, addr_bits; @@ -3228,7 +3228,7 @@ skip_dynamic_range_lvalue_expansion:; AstNode *reg = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(width_hint-1, true), mkconst_int(0, true))); - reg->str = stringf("$past$%s:%d$%d$%d", filename.c_str(), location.first_line, myidx, i); + reg->str = stringf("$past$%s:%d$%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, myidx, i); reg->is_reg = true; reg->is_signed = sign_hint; @@ -3733,7 +3733,7 @@ skip_dynamic_range_lvalue_expansion:; std::stringstream sstr; - sstr << str << "$func$" << filename << ":" << location.first_line << "$" << (autoidx++) << '.'; + sstr << str << "$func$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++) << '.'; std::string prefix = sstr.str(); AstNode *decl = current_scope[str]; @@ -4586,7 +4586,7 @@ static void mark_memories_assign_lhs_complex(dict<AstNode*, pool<std::string>> & if (that->type == AST_IDENTIFIER && that->id2ast && that->id2ast->type == AST_MEMORY) { AstNode *mem = that->id2ast; if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_CMPLX_LHS)) - mem2reg_places[mem].insert(stringf("%s:%d", that->filename.c_str(), that->location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(that->filename).c_str(), that->location.first_line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CMPLX_LHS; } } @@ -4614,14 +4614,14 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg // activate mem2reg if this is assigned in an async proc if (flags & AstNode::MEM2REG_FL_ASYNC) { if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ASYNC)) - mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ASYNC; } // remember if this is assigned blocking (=) if (type == AST_ASSIGN_EQ) { if (!(proc_flags[mem] & AstNode::MEM2REG_FL_EQ1)) - mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); proc_flags[mem] |= AstNode::MEM2REG_FL_EQ1; } @@ -4638,11 +4638,11 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg // remember where this is if (flags & MEM2REG_FL_INIT) { if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_INIT)) - mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_INIT; } else { if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ELSE)) - mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ELSE; } } @@ -4656,7 +4656,7 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg // flag if used after blocking assignment (in same proc) if ((proc_flags[mem] & AstNode::MEM2REG_FL_EQ1) && !(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_EQ2)) { - mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_EQ2; } } @@ -4846,7 +4846,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, children[0]->children[0]->children[0]->type != AST_CONSTANT) { std::stringstream sstr; - sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + sstr << "$mem2reg_wr$" << children[0]->str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA"; int mem_width, mem_size, addr_bits; @@ -4962,7 +4962,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, else { std::stringstream sstr; - sstr << "$mem2reg_rd$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + sstr << "$mem2reg_rd$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA"; int mem_width, mem_size, addr_bits; diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index abf8de4d1..188ef2e04 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -464,6 +464,9 @@ struct LibertyFrontend : public Frontend { log(" -lib\n"); log(" only create empty blackbox modules\n"); log("\n"); + log(" -wb\n"); + log(" mark imported cells as whiteboxes\n"); + log("\n"); log(" -nooverwrite\n"); log(" ignore re-definitions of modules. (the default behavior is to\n"); log(" create an error message if the existing module is not a blackbox\n"); @@ -489,6 +492,7 @@ struct LibertyFrontend : public Frontend { void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override { bool flag_lib = false; + bool flag_wb = false; bool flag_nooverwrite = false; bool flag_overwrite = false; bool flag_ignore_miss_func = false; @@ -505,6 +509,10 @@ struct LibertyFrontend : public Frontend { flag_lib = true; continue; } + if (arg == "-wb") { + flag_wb = true; + continue; + } if (arg == "-ignore_redef" || arg == "-nooverwrite") { flag_nooverwrite = true; flag_overwrite = false; @@ -535,6 +543,9 @@ struct LibertyFrontend : public Frontend { } extra_args(f, filename, args, argidx); + if (flag_wb && flag_lib) + log_error("-wb and -lib cannot be specified together!\n"); + LibertyParser parser(*f); int cell_count = 0; @@ -572,6 +583,9 @@ struct LibertyFrontend : public Frontend { if (flag_lib) module->set_bool_attribute(ID::blackbox); + if (flag_wb) + module->set_bool_attribute(ID::whitebox); + for (auto &attr : attributes) module->attributes[attr] = 1; diff --git a/frontends/verific/README b/frontends/verific/README index 952fb1e0c..921873af3 100644 --- a/frontends/verific/README +++ b/frontends/verific/README @@ -1,7 +1,7 @@ This directory contains Verific bindings for Yosys. -Use Tabby CAD Suite from YosysHQ if you need Yosys+Verifc. +Use Tabby CAD Suite from YosysHQ if you need Yosys+Verific. https://www.yosyshq.com/ Contact YosysHQ at contact@yosyshq.com for free evaluation diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index bbf860c96..e0dbe1b32 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -57,7 +57,7 @@ USING_YOSYS_NAMESPACE #include "FileSystem.h" #ifdef YOSYSHQ_VERIFIC_EXTENSIONS -#include "InitialAssertions.h" +#include "VerificExtensions.h" #endif #ifndef YOSYSHQ_VERIFIC_API_VERSION @@ -183,7 +183,7 @@ RTLIL::IdString VerificImporter::new_verific_id(Verific::DesignObj *obj) { std::string s = stringf("$verific$%s", obj->Name()); if (obj->Linefile()) - s += stringf("$%s:%d", Verific::LineFile::GetFileName(obj->Linefile()), Verific::LineFile::GetLineNo(obj->Linefile())); + s += stringf("$%s:%d", RTLIL::encode_filename(Verific::LineFile::GetFileName(obj->Linefile())).c_str(), Verific::LineFile::GetLineNo(obj->Linefile())); s += stringf("$%d", autoidx++); return s; } @@ -1124,6 +1124,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(portbus->Name()), portbus->Size()); wire->start_offset = min(portbus->LeftIndex(), portbus->RightIndex()); + wire->upto = portbus->IsUp(); import_attributes(wire->attributes, portbus, nl); bool portbus_input = portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN; @@ -1144,7 +1145,8 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma wire->port_output = true; } net = portbus->ElementAtIndex(i)->GetNet(); - RTLIL::SigBit bit(wire, i - wire->start_offset); + int bitidx = wire->upto ? (wire->width - 1 - (i - wire->start_offset)) : (i - wire->start_offset); + RTLIL::SigBit bit(wire, bitidx); if (net_map.count(net) == 0) net_map[net] = bit; else if (bit_input) @@ -1308,6 +1310,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma RTLIL::Wire *wire = module->addWire(wire_name, netbus->Size()); wire->start_offset = min(netbus->LeftIndex(), netbus->RightIndex()); + wire->upto = netbus->IsUp(); MapIter mibus; FOREACH_NET_OF_NETBUS(netbus, mibus, net) { if (net) @@ -1322,7 +1325,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma { if (netbus->ElementAtIndex(i)) { - int bitidx = i - wire->start_offset; + int bitidx = wire->upto ? (wire->width - 1 - (i - wire->start_offset)) : (i - wire->start_offset); net = netbus->ElementAtIndex(i); RTLIL::SigBit bit(wire, bitidx); @@ -2246,7 +2249,7 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par verific_params.Insert(i.first.c_str(), i.second.c_str()); #ifdef YOSYSHQ_VERIFIC_EXTENSIONS - InitialAssertions::Rewrite("work", &verific_params); + VerificExtensions::ElaborateAndRewrite("work", &verific_params); #endif if (top.empty()) { @@ -2312,6 +2315,9 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par nl_todo.erase(it); } +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + VerificExtensions::Reset(); +#endif hier_tree::DeleteHierarchicalTree(); veri_file::Reset(); #ifdef VERIFIC_VHDL_SUPPORT @@ -2494,6 +2500,9 @@ struct VerificPass : public Pass { log(" -v, -vv\n"); log(" Verbose log messages. (-vv is even more verbose than -v.)\n"); log("\n"); + log(" -pp <filename>\n"); + log(" Pretty print design after elaboration to specified file.\n"); + log("\n"); log("The following additional import options are useful for debugging the Verific\n"); log("bindings (for Yosys and/or Verific developers):\n"); log("\n"); @@ -2539,6 +2548,9 @@ struct VerificPass : public Pass { log("Get/set Verific runtime flags.\n"); log("\n"); log("\n"); +#if defined(YOSYS_ENABLE_VERIFIC) and defined(YOSYSHQ_VERIFIC_EXTENSIONS) + VerificExtensions::Help(); +#endif log("Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n"); log("https://www.yosyshq.com/\n"); log("\n"); @@ -2816,9 +2828,11 @@ struct VerificPass : public Pass { for (auto &ext : verific_libexts) veri_file::AddLibExt(ext.c_str()); - while (argidx < GetSize(args)) - file_names.Insert(args[argidx++].c_str()); - + while (argidx < GetSize(args)) { + std::string filename(args[argidx++]); + rewrite_filename(filename); + file_names.Insert(strdup(filename.c_str())); + } if (!veri_file::AnalyzeMultipleFiles(&file_names, verilog_mode, work.c_str(), veri_file::MFCU)) { verific_error_msg.clear(); log_cmd_error("Reading Verilog/SystemVerilog sources failed.\n"); @@ -2831,36 +2845,48 @@ struct VerificPass : public Pass { #ifdef VERIFIC_VHDL_SUPPORT if (GetSize(args) > argidx && args[argidx] == "-vhdl87") { vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1987").c_str()); - for (argidx++; argidx < GetSize(args); argidx++) - if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_87)) - log_cmd_error("Reading `%s' in VHDL_87 mode failed.\n", args[argidx].c_str()); + for (argidx++; argidx < GetSize(args); argidx++) { + std::string filename(args[argidx]); + rewrite_filename(filename); + if (!vhdl_file::Analyze(filename.c_str(), work.c_str(), vhdl_file::VHDL_87)) + log_cmd_error("Reading `%s' in VHDL_87 mode failed.\n", filename.c_str()); + } verific_import_pending = true; goto check_error; } if (GetSize(args) > argidx && args[argidx] == "-vhdl93") { vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str()); - for (argidx++; argidx < GetSize(args); argidx++) - if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_93)) - log_cmd_error("Reading `%s' in VHDL_93 mode failed.\n", args[argidx].c_str()); + for (argidx++; argidx < GetSize(args); argidx++) { + std::string filename(args[argidx]); + rewrite_filename(filename); + if (!vhdl_file::Analyze(filename.c_str(), work.c_str(), vhdl_file::VHDL_93)) + log_cmd_error("Reading `%s' in VHDL_93 mode failed.\n", filename.c_str()); + } verific_import_pending = true; goto check_error; } if (GetSize(args) > argidx && args[argidx] == "-vhdl2k") { vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str()); - for (argidx++; argidx < GetSize(args); argidx++) - if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_2K)) - log_cmd_error("Reading `%s' in VHDL_2K mode failed.\n", args[argidx].c_str()); + for (argidx++; argidx < GetSize(args); argidx++) { + std::string filename(args[argidx]); + rewrite_filename(filename); + if (!vhdl_file::Analyze(filename.c_str(), work.c_str(), vhdl_file::VHDL_2K)) + log_cmd_error("Reading `%s' in VHDL_2K mode failed.\n", filename.c_str()); + } verific_import_pending = true; goto check_error; } if (GetSize(args) > argidx && (args[argidx] == "-vhdl2008" || args[argidx] == "-vhdl")) { vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_2008").c_str()); - for (argidx++; argidx < GetSize(args); argidx++) - if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_2008)) - log_cmd_error("Reading `%s' in VHDL_2008 mode failed.\n", args[argidx].c_str()); + for (argidx++; argidx < GetSize(args); argidx++) { + std::string filename(args[argidx]); + rewrite_filename(filename); + if (!vhdl_file::Analyze(filename.c_str(), work.c_str(), vhdl_file::VHDL_2008)) + log_cmd_error("Reading `%s' in VHDL_2008 mode failed.\n", filename.c_str()); + } verific_import_pending = true; goto check_error; } @@ -2922,6 +2948,7 @@ struct VerificPass : public Pass { bool mode_autocover = false, mode_fullinit = false; bool flatten = false, extnets = false; string dumpfile; + string ppfile; Map parameters(STRING_HASH); for (argidx++; argidx < GetSize(args); argidx++) { @@ -2990,6 +3017,10 @@ struct VerificPass : public Pass { dumpfile = args[++argidx]; continue; } + if (args[argidx] == "-pp" && argidx+1 < GetSize(args)) { + ppfile = args[++argidx]; + continue; + } break; } @@ -2999,8 +3030,11 @@ struct VerificPass : public Pass { std::set<std::string> top_mod_names; #ifdef YOSYSHQ_VERIFIC_EXTENSIONS - InitialAssertions::Rewrite(work, ¶meters); + VerificExtensions::ElaborateAndRewrite(work, ¶meters); #endif + if (!ppfile.empty()) + veri_file::PrettyPrint(ppfile.c_str(), nullptr, work.c_str()); + if (mode_all) { log("Running hier_tree::ElaborateAll().\n"); @@ -3113,6 +3147,9 @@ struct VerificPass : public Pass { nl_todo.erase(it); } +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + VerificExtensions::Reset(); +#endif hier_tree::DeleteHierarchicalTree(); veri_file::Reset(); #ifdef VERIFIC_VHDL_SUPPORT @@ -3187,6 +3224,13 @@ struct VerificPass : public Pass { } } } +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + if (VerificExtensions::Execute(args, argidx, work, + [this](const std::vector<std::string> &args, size_t argidx, std::string msg) + { cmd_error(args, argidx, msg); } )) { + goto check_error; + } +#endif cmd_error(args, argidx, "Missing or unsupported mode parameter.\n"); diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index 883531e78..e33b0a2c3 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -961,6 +961,11 @@ frontend_verilog_preproc(std::istream &f, } if (tok == "`resetall") { + default_nettype_wire = true; + continue; + } + + if (tok == "`undefineall" && sv_mode) { defines.clear(); global_defines_cache.clear(); continue; diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 7e9cfb38d..d62ba1506 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -51,6 +51,7 @@ struct CellTypes setup_internals(); setup_internals_mem(); + setup_internals_anyinit(); setup_stdcells(); setup_stdcells_mem(); } @@ -155,6 +156,11 @@ struct CellTypes setup_type(ID($dlatchsr), {ID::EN, ID::SET, ID::CLR, ID::D}, {ID::Q}); } + void setup_internals_anyinit() + { + setup_type(ID($anyinit), {ID::D}, {ID::Q}); + } + void setup_internals_mem() { setup_internals_ff(); diff --git a/kernel/constids.inc b/kernel/constids.inc index 0f6dfc29b..239381f85 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -171,6 +171,7 @@ X(RD_TRANSPARENCY_MASK) X(RD_TRANSPARENT) X(RD_WIDE_CONTINUATION) X(reg) +X(replaced_by_gclk) X(reprocess_after) X(rom_block) X(rom_style) diff --git a/kernel/driver.cc b/kernel/driver.cc index f8f940e89..e52e1fb0e 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -192,6 +192,13 @@ void yosys_atexit() #endif } +#if defined(__OpenBSD__) +namespace Yosys { +extern char *yosys_argv0; +extern char yosys_path[PATH_MAX]; +}; +#endif + int main(int argc, char **argv) { std::string frontend_command = "auto"; @@ -498,6 +505,12 @@ int main(int argc, char **argv) if (print_stats) log_hasher = new SHA1; +#if defined(__OpenBSD__) + // save the executable origin for proc_self_dirname() + yosys_argv0 = argv[0]; + realpath(yosys_argv0, yosys_path); +#endif + #if defined(__linux__) // set stack size to >= 128 MB { diff --git a/kernel/ff.cc b/kernel/ff.cc index b0f1a924f..697ba7342 100644 --- a/kernel/ff.cc +++ b/kernel/ff.cc @@ -33,10 +33,14 @@ FfData::FfData(FfInitVals *initvals, Cell *cell_) : FfData(cell_->module, initva std::string type_str = cell->type.str(); - if (cell->type.in(ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) { - if (cell->type == ID($ff)) { + if (cell->type.in(ID($anyinit), ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) { + if (cell->type.in(ID($anyinit), ID($ff))) { has_gclk = true; sig_d = cell->getPort(ID::D); + if (cell->type == ID($anyinit)) { + is_anyinit = true; + log_assert(val_init.is_fully_undef()); + } } else if (cell->type == ID($sr)) { // No data input at all. } else if (cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr))) { @@ -274,6 +278,7 @@ FfData FfData::slice(const std::vector<int> &bits) { res.has_sr = has_sr; res.ce_over_srst = ce_over_srst; res.is_fine = is_fine; + res.is_anyinit = is_anyinit; res.pol_clk = pol_clk; res.pol_ce = pol_ce; res.pol_aload = pol_aload; @@ -542,7 +547,7 @@ Cell *FfData::emit() { return nullptr; } } - if (initvals) + if (initvals && !is_anyinit) initvals->set_init(sig_q, val_init); if (!is_fine) { if (has_gclk) { @@ -552,7 +557,12 @@ Cell *FfData::emit() { log_assert(!has_arst); log_assert(!has_srst); log_assert(!has_sr); - cell = module->addFf(name, sig_d, sig_q); + if (is_anyinit) { + cell = module->addAnyinit(name, sig_d, sig_q); + log_assert(val_init.is_fully_undef()); + } else { + cell = module->addFf(name, sig_d, sig_q); + } } else if (!has_aload && !has_clk) { log_assert(has_sr); cell = module->addSr(name, sig_set, sig_clr, sig_q, pol_set, pol_clr); @@ -603,6 +613,7 @@ Cell *FfData::emit() { log_assert(!has_arst); log_assert(!has_srst); log_assert(!has_sr); + log_assert(!is_anyinit); cell = module->addFfGate(name, sig_d, sig_q); } else if (!has_aload && !has_clk) { log_assert(has_sr); diff --git a/kernel/ff.h b/kernel/ff.h index 41721b4a1..e684d3c43 100644 --- a/kernel/ff.h +++ b/kernel/ff.h @@ -28,7 +28,10 @@ YOSYS_NAMESPACE_BEGIN // Describes a flip-flop or a latch. // // If has_gclk, this is a formal verification FF with implicit global clock: -// Q is simply previous cycle's D. +// Q is simply previous cycle's D. Additionally if is_anyinit is true, this is +// an $anyinit cell which always has an undefined initialization value. Note +// that $anyinit is not considered to be among the FF celltypes, so a pass has +// to explicitly opt-in to process $anyinit cells with FfData. // // Otherwise, the FF/latch can have any number of features selected by has_* // attributes that determine Q's value (in order of decreasing priority): @@ -126,6 +129,8 @@ struct FfData { // True if this FF is a fine cell, false if it is a coarse cell. // If true, width must be 1. bool is_fine; + // True if this FF is an $anyinit cell. Depends on has_gclk. + bool is_anyinit; // Polarities, corresponding to sig_*. True means active-high, false // means active-low. bool pol_clk; @@ -156,6 +161,7 @@ struct FfData { has_sr = false; ce_over_srst = false; is_fine = false; + is_anyinit = false; pol_clk = false; pol_aload = false; pol_ce = false; diff --git a/kernel/log.cc b/kernel/log.cc index 4bcce3b28..4403dd0c7 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -627,7 +627,7 @@ const char *log_const(const RTLIL::Const &value, bool autoint) } } -const char *log_id(RTLIL::IdString str) +const char *log_id(const RTLIL::IdString &str) { log_id_cache.push_back(strdup(str.c_str())); const char *p = log_id_cache.back(); diff --git a/kernel/log.h b/kernel/log.h index ea14028dd..3bc9fd978 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -237,7 +237,7 @@ void log_check_expected(); const char *log_signal(const RTLIL::SigSpec &sig, bool autoint = true); const char *log_const(const RTLIL::Const &value, bool autoint = true); -const char *log_id(RTLIL::IdString id); +const char *log_id(const RTLIL::IdString &id); template<typename T> static inline const char *log_id(T *obj, const char *nullstr = nullptr) { if (nullstr && obj == nullptr) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index dc4ea9a78..5211c3b3f 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -199,11 +199,6 @@ const pool<IdString> &RTLIL::builtin_ff_cell_types() { return res; } -RTLIL::Const::Const() -{ - flags = RTLIL::CONST_FLAG_NONE; -} - RTLIL::Const::Const(const std::string &str) { flags = RTLIL::CONST_FLAG_STRING; @@ -395,12 +390,12 @@ bool RTLIL::Const::is_onehot(int *pos) const return found; } -bool RTLIL::AttrObject::has_attribute(RTLIL::IdString id) const +bool RTLIL::AttrObject::has_attribute(const RTLIL::IdString &id) const { return attributes.count(id); } -void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id, bool value) +void RTLIL::AttrObject::set_bool_attribute(const RTLIL::IdString &id, bool value) { if (value) attributes[id] = RTLIL::Const(1); @@ -408,7 +403,7 @@ void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id, bool value) attributes.erase(id); } -bool RTLIL::AttrObject::get_bool_attribute(RTLIL::IdString id) const +bool RTLIL::AttrObject::get_bool_attribute(const RTLIL::IdString &id) const { const auto it = attributes.find(id); if (it == attributes.end()) @@ -416,7 +411,7 @@ bool RTLIL::AttrObject::get_bool_attribute(RTLIL::IdString id) const return it->second.as_bool(); } -void RTLIL::AttrObject::set_string_attribute(RTLIL::IdString id, string value) +void RTLIL::AttrObject::set_string_attribute(const RTLIL::IdString& id, string value) { if (value.empty()) attributes.erase(id); @@ -424,7 +419,7 @@ void RTLIL::AttrObject::set_string_attribute(RTLIL::IdString id, string value) attributes[id] = value; } -string RTLIL::AttrObject::get_string_attribute(RTLIL::IdString id) const +string RTLIL::AttrObject::get_string_attribute(const RTLIL::IdString &id) const { std::string value; const auto it = attributes.find(id); @@ -433,7 +428,7 @@ string RTLIL::AttrObject::get_string_attribute(RTLIL::IdString id) const return value; } -void RTLIL::AttrObject::set_strpool_attribute(RTLIL::IdString id, const pool<string> &data) +void RTLIL::AttrObject::set_strpool_attribute(const RTLIL::IdString& id, const pool<string> &data) { string attrval; for (const auto &s : data) { @@ -444,7 +439,7 @@ void RTLIL::AttrObject::set_strpool_attribute(RTLIL::IdString id, const pool<str set_string_attribute(id, attrval); } -void RTLIL::AttrObject::add_strpool_attribute(RTLIL::IdString id, const pool<string> &data) +void RTLIL::AttrObject::add_strpool_attribute(const RTLIL::IdString& id, const pool<string> &data) { pool<string> union_data = get_strpool_attribute(id); union_data.insert(data.begin(), data.end()); @@ -452,7 +447,7 @@ void RTLIL::AttrObject::add_strpool_attribute(RTLIL::IdString id, const pool<str set_strpool_attribute(id, union_data); } -pool<string> RTLIL::AttrObject::get_strpool_attribute(RTLIL::IdString id) const +pool<string> RTLIL::AttrObject::get_strpool_attribute(const RTLIL::IdString &id) const { pool<string> data; if (attributes.count(id) != 0) @@ -477,7 +472,7 @@ vector<string> RTLIL::AttrObject::get_hdlname_attribute() const return split_tokens(get_string_attribute(ID::hdlname), " "); } -void RTLIL::AttrObject::set_intvec_attribute(RTLIL::IdString id, const vector<int> &data) +void RTLIL::AttrObject::set_intvec_attribute(const RTLIL::IdString& id, const vector<int> &data) { std::stringstream attrval; for (auto &i : data) { @@ -488,7 +483,7 @@ void RTLIL::AttrObject::set_intvec_attribute(RTLIL::IdString id, const vector<in attributes[id] = RTLIL::Const(attrval.str()); } -vector<int> RTLIL::AttrObject::get_intvec_attribute(RTLIL::IdString id) const +vector<int> RTLIL::AttrObject::get_intvec_attribute(const RTLIL::IdString &id) const { vector<int> data; auto it = attributes.find(id); @@ -506,7 +501,7 @@ vector<int> RTLIL::AttrObject::get_intvec_attribute(RTLIL::IdString id) const return data; } -bool RTLIL::Selection::selected_module(RTLIL::IdString mod_name) const +bool RTLIL::Selection::selected_module(const RTLIL::IdString &mod_name) const { if (full_selection) return true; @@ -517,7 +512,7 @@ bool RTLIL::Selection::selected_module(RTLIL::IdString mod_name) const return false; } -bool RTLIL::Selection::selected_whole_module(RTLIL::IdString mod_name) const +bool RTLIL::Selection::selected_whole_module(const RTLIL::IdString &mod_name) const { if (full_selection) return true; @@ -526,7 +521,7 @@ bool RTLIL::Selection::selected_whole_module(RTLIL::IdString mod_name) const return false; } -bool RTLIL::Selection::selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const +bool RTLIL::Selection::selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const { if (full_selection) return true; @@ -638,12 +633,12 @@ RTLIL::ObjRange<RTLIL::Module*> RTLIL::Design::modules() return RTLIL::ObjRange<RTLIL::Module*>(&modules_, &refcount_modules_); } -RTLIL::Module *RTLIL::Design::module(RTLIL::IdString name) +RTLIL::Module *RTLIL::Design::module(const RTLIL::IdString& name) { return modules_.count(name) ? modules_.at(name) : NULL; } -const RTLIL::Module *RTLIL::Design::module(RTLIL::IdString name) const +const RTLIL::Module *RTLIL::Design::module(const RTLIL::IdString& name) const { return modules_.count(name) ? modules_.at(name) : NULL; } @@ -825,7 +820,7 @@ void RTLIL::Design::optimize() it.second.optimize(this); } -bool RTLIL::Design::selected_module(RTLIL::IdString mod_name) const +bool RTLIL::Design::selected_module(const RTLIL::IdString& mod_name) const { if (!selected_active_module.empty() && mod_name != selected_active_module) return false; @@ -834,7 +829,7 @@ bool RTLIL::Design::selected_module(RTLIL::IdString mod_name) const return selection_stack.back().selected_module(mod_name); } -bool RTLIL::Design::selected_whole_module(RTLIL::IdString mod_name) const +bool RTLIL::Design::selected_whole_module(const RTLIL::IdString& mod_name) const { if (!selected_active_module.empty() && mod_name != selected_active_module) return false; @@ -843,7 +838,7 @@ bool RTLIL::Design::selected_whole_module(RTLIL::IdString mod_name) const return selection_stack.back().selected_whole_module(mod_name); } -bool RTLIL::Design::selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const +bool RTLIL::Design::selected_member(const RTLIL::IdString& mod_name, const RTLIL::IdString& memb_name) const { if (!selected_active_module.empty() && mod_name != selected_active_module) return false; @@ -987,7 +982,7 @@ RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, const dict<RTLIL::IdString log_error("Module `%s' is used with parameters but is not parametric!\n", id2cstr(name)); } -size_t RTLIL::Module::count_id(RTLIL::IdString id) +size_t RTLIL::Module::count_id(const RTLIL::IdString& id) { return wires_.count(id) + memories.count(id) + cells_.count(id) + processes.count(id); } @@ -1012,7 +1007,7 @@ namespace { cell->name.c_str(), cell->type.c_str(), __FILE__, linenr, buf.str().c_str()); } - int param(RTLIL::IdString name) + int param(const RTLIL::IdString& name) { auto it = cell->parameters.find(name); if (it == cell->parameters.end()) @@ -1021,7 +1016,7 @@ namespace { return it->second.as_int(); } - int param_bool(RTLIL::IdString name) + int param_bool(const RTLIL::IdString& name) { int v = param(name); if (GetSize(cell->parameters.at(name)) > 32) @@ -1031,7 +1026,7 @@ namespace { return v; } - int param_bool(RTLIL::IdString name, bool expected) + int param_bool(const RTLIL::IdString& name, bool expected) { int v = param_bool(name); if (v != expected) @@ -1039,14 +1034,14 @@ namespace { return v; } - void param_bits(RTLIL::IdString name, int width) + void param_bits(const RTLIL::IdString& name, int width) { param(name); if (GetSize(cell->parameters.at(name).bits) != width) error(__LINE__); } - void port(RTLIL::IdString name, int width) + void port(const RTLIL::IdString& name, int width) { auto it = cell->connections_.find(name); if (it == cell->connections_.end()) @@ -1637,6 +1632,13 @@ namespace { return; } + if (cell->type.in(ID($anyinit))) { + port(ID::D, param(ID::WIDTH)); + port(ID::Q, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($equiv)) { port(ID::A, 1); port(ID::B, 1); @@ -3125,6 +3127,16 @@ RTLIL::Cell* RTLIL::Module::addDlatchsrGate(RTLIL::IdString name, const RTLIL::S return cell; } +RTLIL::Cell* RTLIL::Module::addAnyinit(RTLIL::IdString name, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, ID($anyinit)); + cell->parameters[ID::WIDTH] = sig_q.size(); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::Q, sig_q); + cell->set_src_attribute(src); + return cell; +} + RTLIL::SigSpec RTLIL::Module::Anyconst(RTLIL::IdString name, int width, const std::string &src) { RTLIL::SigSpec sig = addWire(NEW_ID, width); @@ -3259,12 +3271,12 @@ std::map<unsigned int, RTLIL::Cell*> *RTLIL::Cell::get_all_cells(void) } #endif -bool RTLIL::Cell::hasPort(RTLIL::IdString portname) const +bool RTLIL::Cell::hasPort(const RTLIL::IdString& portname) const { return connections_.count(portname) != 0; } -void RTLIL::Cell::unsetPort(RTLIL::IdString portname) +void RTLIL::Cell::unsetPort(const RTLIL::IdString& portname) { RTLIL::SigSpec signal; auto conn_it = connections_.find(portname); @@ -3287,7 +3299,7 @@ void RTLIL::Cell::unsetPort(RTLIL::IdString portname) } } -void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal) +void RTLIL::Cell::setPort(const RTLIL::IdString& portname, RTLIL::SigSpec signal) { auto r = connections_.insert(portname); auto conn_it = r.first; @@ -3309,7 +3321,7 @@ void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal) conn_it->second = std::move(signal); } -const RTLIL::SigSpec &RTLIL::Cell::getPort(RTLIL::IdString portname) const +const RTLIL::SigSpec &RTLIL::Cell::getPort(const RTLIL::IdString& portname) const { return connections_.at(portname); } @@ -3328,7 +3340,7 @@ bool RTLIL::Cell::known() const return false; } -bool RTLIL::Cell::input(RTLIL::IdString portname) const +bool RTLIL::Cell::input(const RTLIL::IdString& portname) const { if (yosys_celltypes.cell_known(type)) return yosys_celltypes.cell_input(type, portname); @@ -3340,7 +3352,7 @@ bool RTLIL::Cell::input(RTLIL::IdString portname) const return false; } -bool RTLIL::Cell::output(RTLIL::IdString portname) const +bool RTLIL::Cell::output(const RTLIL::IdString& portname) const { if (yosys_celltypes.cell_known(type)) return yosys_celltypes.cell_output(type, portname); @@ -3352,22 +3364,22 @@ bool RTLIL::Cell::output(RTLIL::IdString portname) const return false; } -bool RTLIL::Cell::hasParam(RTLIL::IdString paramname) const +bool RTLIL::Cell::hasParam(const RTLIL::IdString& paramname) const { return parameters.count(paramname) != 0; } -void RTLIL::Cell::unsetParam(RTLIL::IdString paramname) +void RTLIL::Cell::unsetParam(const RTLIL::IdString& paramname) { parameters.erase(paramname); } -void RTLIL::Cell::setParam(RTLIL::IdString paramname, RTLIL::Const value) +void RTLIL::Cell::setParam(const RTLIL::IdString& paramname, RTLIL::Const value) { parameters[paramname] = std::move(value); } -const RTLIL::Const &RTLIL::Cell::getParam(RTLIL::IdString paramname) const +const RTLIL::Const &RTLIL::Cell::getParam(const RTLIL::IdString& paramname) const { const auto &it = parameters.find(paramname); if (it != parameters.end()) @@ -3472,61 +3484,6 @@ bool RTLIL::Cell::is_mem_cell() const return type.in(ID($mem), ID($mem_v2)) || has_memid(); } -RTLIL::SigChunk::SigChunk() -{ - wire = NULL; - width = 0; - offset = 0; -} - -RTLIL::SigChunk::SigChunk(const RTLIL::Const &value) -{ - wire = NULL; - data = value.bits; - width = GetSize(data); - offset = 0; -} - -RTLIL::SigChunk::SigChunk(RTLIL::Wire *wire) -{ - log_assert(wire != nullptr); - this->wire = wire; - this->width = wire->width; - this->offset = 0; -} - -RTLIL::SigChunk::SigChunk(RTLIL::Wire *wire, int offset, int width) -{ - log_assert(wire != nullptr); - this->wire = wire; - this->width = width; - this->offset = offset; -} - -RTLIL::SigChunk::SigChunk(const std::string &str) -{ - wire = NULL; - data = RTLIL::Const(str).bits; - width = GetSize(data); - offset = 0; -} - -RTLIL::SigChunk::SigChunk(int val, int width) -{ - wire = NULL; - data = RTLIL::Const(val, width).bits; - this->width = GetSize(data); - offset = 0; -} - -RTLIL::SigChunk::SigChunk(RTLIL::State bit, int width) -{ - wire = NULL; - data = RTLIL::Const(bit, width).bits; - this->width = GetSize(data); - offset = 0; -} - RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit) { wire = bit.wire; @@ -3538,11 +3495,6 @@ RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit) width = 1; } -RTLIL::SigChunk::SigChunk(const RTLIL::SigChunk &sigchunk) -{ - *this = sigchunk; -} - RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const { RTLIL::SigChunk ret; @@ -3588,17 +3540,6 @@ bool RTLIL::SigChunk::operator !=(const RTLIL::SigChunk &other) const return true; } -RTLIL::SigSpec::SigSpec() -{ - width_ = 0; - hash_ = 0; -} - -RTLIL::SigSpec::SigSpec(const RTLIL::SigSpec &other) -{ - *this = other; -} - RTLIL::SigSpec::SigSpec(std::initializer_list<RTLIL::SigSpec> parts) { cover("kernel.rtlil.sigspec.init.list"); @@ -3613,23 +3554,26 @@ RTLIL::SigSpec::SigSpec(std::initializer_list<RTLIL::SigSpec> parts) append(*it--); } -RTLIL::SigSpec &RTLIL::SigSpec::operator=(const RTLIL::SigSpec &other) +RTLIL::SigSpec::SigSpec(const RTLIL::Const &value) { - cover("kernel.rtlil.sigspec.assign"); + cover("kernel.rtlil.sigspec.init.const"); - width_ = other.width_; - hash_ = other.hash_; - chunks_ = other.chunks_; - bits_ = other.bits_; - return *this; + if (GetSize(value) != 0) { + chunks_.emplace_back(value); + width_ = chunks_.back().width; + } else { + width_ = 0; + } + hash_ = 0; + check(); } -RTLIL::SigSpec::SigSpec(const RTLIL::Const &value) +RTLIL::SigSpec::SigSpec(RTLIL::Const &&value) { - cover("kernel.rtlil.sigspec.init.const"); + cover("kernel.rtlil.sigspec.init.const.move"); if (GetSize(value) != 0) { - chunks_.emplace_back(value); + chunks_.emplace_back(std::move(value)); width_ = chunks_.back().width; } else { width_ = 0; @@ -3652,6 +3596,20 @@ RTLIL::SigSpec::SigSpec(const RTLIL::SigChunk &chunk) check(); } +RTLIL::SigSpec::SigSpec(RTLIL::SigChunk &&chunk) +{ + cover("kernel.rtlil.sigspec.init.chunk.move"); + + if (chunk.width != 0) { + chunks_.emplace_back(std::move(chunk)); + width_ = chunks_.back().width; + } else { + width_ = 0; + } + hash_ = 0; + check(); +} + RTLIL::SigSpec::SigSpec(RTLIL::Wire *wire) { cover("kernel.rtlil.sigspec.init.wire"); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 7a0b6b9c7..27ffdff1f 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -168,7 +168,7 @@ namespace RTLIL log_assert(p[1] != 0); for (const char *c = p; *c; c++) if ((unsigned)*c <= (unsigned)' ') - log_error("Found control character or space (0x%02hhx) in string '%s' which is not allowed in RTLIL identifiers\n", *c, p); + log_error("Found control character or space (0x%02x) in string '%s' which is not allowed in RTLIL identifiers\n", *c, p); #ifndef YOSYS_NO_IDS_REFCNT if (global_free_idx_list_.empty()) { @@ -414,11 +414,11 @@ namespace RTLIL return str.substr(1); } - static inline std::string unescape_id(RTLIL::IdString str) { + static inline std::string unescape_id(const RTLIL::IdString &str) { return unescape_id(str.str()); } - static inline const char *id2cstr(RTLIL::IdString str) { + static inline const char *id2cstr(const RTLIL::IdString &str) { return log_id(str); } @@ -435,11 +435,26 @@ namespace RTLIL }; struct sort_by_id_str { - bool operator()(RTLIL::IdString a, RTLIL::IdString b) const { + bool operator()(const RTLIL::IdString &a, const RTLIL::IdString &b) const { return strcmp(a.c_str(), b.c_str()) < 0; } }; + static inline std::string encode_filename(const std::string &filename) + { + std::stringstream val; + if (!std::any_of(filename.begin(), filename.end(), [](char c) { + return static_cast<unsigned char>(c) < 33 || static_cast<unsigned char>(c) > 126; + })) return filename; + for (unsigned char const c : filename) { + if (c < 33 || c > 126) + val << stringf("$%02x", c); + else + val << c; + } + return val.str(); + } + // see calc.cc for the implementation of this functions RTLIL::Const const_not (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); RTLIL::Const const_and (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); @@ -635,7 +650,7 @@ struct RTLIL::Const int flags; std::vector<RTLIL::State> bits; - Const(); + Const() : flags(RTLIL::CONST_FLAG_NONE) {} Const(const std::string &str); Const(int val, int width = 32); Const(RTLIL::State bit, int width = 1); @@ -696,21 +711,21 @@ struct RTLIL::AttrObject { dict<RTLIL::IdString, RTLIL::Const> attributes; - bool has_attribute(RTLIL::IdString id) const; + bool has_attribute(const RTLIL::IdString &id) const; - void set_bool_attribute(RTLIL::IdString id, bool value=true); - bool get_bool_attribute(RTLIL::IdString id) const; + void set_bool_attribute(const RTLIL::IdString &id, bool value=true); + bool get_bool_attribute(const RTLIL::IdString &id) const; bool get_blackbox_attribute(bool ignore_wb=false) const { return get_bool_attribute(ID::blackbox) || (!ignore_wb && get_bool_attribute(ID::whitebox)); } - void set_string_attribute(RTLIL::IdString id, string value); - string get_string_attribute(RTLIL::IdString id) const; + void set_string_attribute(const RTLIL::IdString& id, string value); + string get_string_attribute(const RTLIL::IdString &id) const; - void set_strpool_attribute(RTLIL::IdString id, const pool<string> &data); - void add_strpool_attribute(RTLIL::IdString id, const pool<string> &data); - pool<string> get_strpool_attribute(RTLIL::IdString id) const; + void set_strpool_attribute(const RTLIL::IdString& id, const pool<string> &data); + void add_strpool_attribute(const RTLIL::IdString& id, const pool<string> &data); + pool<string> get_strpool_attribute(const RTLIL::IdString &id) const; void set_src_attribute(const std::string &src) { set_string_attribute(ID::src, src); @@ -722,8 +737,8 @@ struct RTLIL::AttrObject void set_hdlname_attribute(const vector<string> &hierarchy); vector<string> get_hdlname_attribute() const; - void set_intvec_attribute(RTLIL::IdString id, const vector<int> &data); - vector<int> get_intvec_attribute(RTLIL::IdString id) const; + void set_intvec_attribute(const RTLIL::IdString& id, const vector<int> &data); + vector<int> get_intvec_attribute(const RTLIL::IdString &id) const; }; struct RTLIL::SigChunk @@ -732,16 +747,15 @@ struct RTLIL::SigChunk std::vector<RTLIL::State> data; // only used if wire == NULL, LSB at index 0 int width, offset; - SigChunk(); - SigChunk(const RTLIL::Const &value); - SigChunk(RTLIL::Wire *wire); - SigChunk(RTLIL::Wire *wire, int offset, int width = 1); - SigChunk(const std::string &str); - SigChunk(int val, int width = 32); - SigChunk(RTLIL::State bit, int width = 1); + SigChunk() : wire(nullptr), width(0), offset(0) {} + SigChunk(const RTLIL::Const &value) : wire(nullptr), data(value.bits), width(GetSize(data)), offset(0) {} + SigChunk(RTLIL::Const &&value) : wire(nullptr), data(std::move(value.bits)), width(GetSize(data)), offset(0) {} + SigChunk(RTLIL::Wire *wire) : wire(wire), width(GetSize(wire)), offset(0) {} + SigChunk(RTLIL::Wire *wire, int offset, int width = 1) : wire(wire), width(width), offset(offset) {} + SigChunk(const std::string &str) : SigChunk(RTLIL::Const(str)) {} + SigChunk(int val, int width = 32) : SigChunk(RTLIL::Const(val, width)) {} + SigChunk(RTLIL::State bit, int width = 1) : SigChunk(RTLIL::Const(bit, width)) {} SigChunk(const RTLIL::SigBit &bit); - SigChunk(const RTLIL::SigChunk &sigchunk); - RTLIL::SigChunk &operator =(const RTLIL::SigChunk &other) = default; RTLIL::SigChunk extract(int offset, int length) const; inline int size() const { return width; } @@ -827,13 +841,13 @@ private: friend struct RTLIL::Module; public: - SigSpec(); - SigSpec(const RTLIL::SigSpec &other); + SigSpec() : width_(0), hash_(0) {} SigSpec(std::initializer_list<RTLIL::SigSpec> parts); - RTLIL::SigSpec &operator=(const RTLIL::SigSpec &other); SigSpec(const RTLIL::Const &value); + SigSpec(RTLIL::Const &&value); SigSpec(const RTLIL::SigChunk &chunk); + SigSpec(RTLIL::SigChunk &&chunk); SigSpec(RTLIL::Wire *wire); SigSpec(RTLIL::Wire *wire, int offset, int width = 1); SigSpec(const std::string &str); @@ -846,21 +860,6 @@ public: SigSpec(const std::set<RTLIL::SigBit> &bits); explicit SigSpec(bool bit); - SigSpec(RTLIL::SigSpec &&other) { - width_ = other.width_; - hash_ = other.hash_; - chunks_ = std::move(other.chunks_); - bits_ = std::move(other.bits_); - } - - const RTLIL::SigSpec &operator=(RTLIL::SigSpec &&other) { - width_ = other.width_; - hash_ = other.hash_; - chunks_ = std::move(other.chunks_); - bits_ = std::move(other.bits_); - return *this; - } - size_t get_hash() const { if (!hash_) hash(); return hash_; @@ -985,9 +984,9 @@ struct RTLIL::Selection Selection(bool full = true) : full_selection(full) { } - bool selected_module(RTLIL::IdString mod_name) const; - bool selected_whole_module(RTLIL::IdString mod_name) const; - bool selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const; + bool selected_module(const RTLIL::IdString &mod_name) const; + bool selected_whole_module(const RTLIL::IdString &mod_name) const; + bool selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const; void optimize(RTLIL::Design *design); template<typename T1> void select(T1 *module) { @@ -1053,11 +1052,11 @@ struct RTLIL::Design ~Design(); RTLIL::ObjRange<RTLIL::Module*> modules(); - RTLIL::Module *module(RTLIL::IdString name); - const RTLIL::Module *module(RTLIL::IdString name) const; + RTLIL::Module *module(const RTLIL::IdString &name); + const RTLIL::Module *module(const RTLIL::IdString &name) const; RTLIL::Module *top_module(); - bool has(RTLIL::IdString id) const { + bool has(const RTLIL::IdString &id) const { return modules_.count(id) != 0; } @@ -1082,9 +1081,9 @@ struct RTLIL::Design void check(); void optimize(); - bool selected_module(RTLIL::IdString mod_name) const; - bool selected_whole_module(RTLIL::IdString mod_name) const; - bool selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const; + bool selected_module(const RTLIL::IdString &mod_name) const; + bool selected_whole_module(const RTLIL::IdString &mod_name) const; + bool selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const; bool selected_module(RTLIL::Module *mod) const; bool selected_whole_module(RTLIL::Module *mod) const; @@ -1165,7 +1164,7 @@ public: virtual ~Module(); virtual RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, bool mayfail = false); virtual RTLIL::IdString derive(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, const dict<RTLIL::IdString, RTLIL::Module*> &interfaces, const dict<RTLIL::IdString, RTLIL::IdString> &modports, bool mayfail = false); - virtual size_t count_id(RTLIL::IdString id); + virtual size_t count_id(const RTLIL::IdString& id); virtual void expand_interfaces(RTLIL::Design *design, const dict<RTLIL::IdString, RTLIL::Module *> &local_interfaces); virtual bool reprocess_if_necessary(RTLIL::Design *design); @@ -1200,20 +1199,20 @@ public: return design->selected_member(name, member->name); } - RTLIL::Wire* wire(RTLIL::IdString id) { + RTLIL::Wire* wire(const RTLIL::IdString &id) { auto it = wires_.find(id); return it == wires_.end() ? nullptr : it->second; } - RTLIL::Cell* cell(RTLIL::IdString id) { + RTLIL::Cell* cell(const RTLIL::IdString &id) { auto it = cells_.find(id); return it == cells_.end() ? nullptr : it->second; } - const RTLIL::Wire* wire(RTLIL::IdString id) const{ + const RTLIL::Wire* wire(const RTLIL::IdString &id) const{ auto it = wires_.find(id); return it == wires_.end() ? nullptr : it->second; } - const RTLIL::Cell* cell(RTLIL::IdString id) const { + const RTLIL::Cell* cell(const RTLIL::IdString &id) const { auto it = cells_.find(id); return it == cells_.end() ? nullptr : it->second; } @@ -1376,6 +1375,8 @@ public: RTLIL::Cell* addDlatchsrGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_set, const RTLIL::SigSpec &sig_clr, RTLIL::SigSpec sig_d, const RTLIL::SigSpec &sig_q, bool en_polarity = true, bool set_polarity = true, bool clr_polarity = true, const std::string &src = ""); + RTLIL::Cell* addAnyinit(RTLIL::IdString name, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, const std::string &src = ""); + // The methods without the add* prefix create a cell and an output signal. They return the newly created output signal. RTLIL::SigSpec Not (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); @@ -1483,6 +1484,10 @@ public: #endif }; +inline int GetSize(RTLIL::Wire *wire) { + return wire->width; +} + struct RTLIL::Memory : public RTLIL::AttrObject { unsigned int hashidx_; @@ -1521,22 +1526,22 @@ public: dict<RTLIL::IdString, RTLIL::Const> parameters; // access cell ports - bool hasPort(RTLIL::IdString portname) const; - void unsetPort(RTLIL::IdString portname); - void setPort(RTLIL::IdString portname, RTLIL::SigSpec signal); - const RTLIL::SigSpec &getPort(RTLIL::IdString portname) const; + bool hasPort(const RTLIL::IdString &portname) const; + void unsetPort(const RTLIL::IdString &portname); + void setPort(const RTLIL::IdString &portname, RTLIL::SigSpec signal); + const RTLIL::SigSpec &getPort(const RTLIL::IdString &portname) const; const dict<RTLIL::IdString, RTLIL::SigSpec> &connections() const; // information about cell ports bool known() const; - bool input(RTLIL::IdString portname) const; - bool output(RTLIL::IdString portname) const; + bool input(const RTLIL::IdString &portname) const; + bool output(const RTLIL::IdString &portname) const; // access cell parameters - bool hasParam(RTLIL::IdString paramname) const; - void unsetParam(RTLIL::IdString paramname); - void setParam(RTLIL::IdString paramname, RTLIL::Const value); - const RTLIL::Const &getParam(RTLIL::IdString paramname) const; + bool hasParam(const RTLIL::IdString ¶mname) const; + void unsetParam(const RTLIL::IdString ¶mname); + void setParam(const RTLIL::IdString ¶mname, RTLIL::Const value); + const RTLIL::Const &getParam(const RTLIL::IdString ¶mname) const; void sort(); void check(); diff --git a/kernel/satgen.cc b/kernel/satgen.cc index 9c40ec66d..05eeca76e 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -1176,7 +1176,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) return true; } - if (timestep > 0 && RTLIL::builtin_ff_cell_types().count(cell->type)) + if (timestep > 0 && (RTLIL::builtin_ff_cell_types().count(cell->type) || cell->type == ID($anyinit))) { FfData ff(nullptr, cell); diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 64d2b4def..a56a066fe 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -534,11 +534,6 @@ std::string escape_filename_spaces(const std::string& filename) return out; } -int GetSize(RTLIL::Wire *wire) -{ - return wire->width; -} - bool already_setup = false; void yosys_setup() @@ -774,6 +769,10 @@ struct TclPass : public Pass { log("If any arguments are specified, these arguments are provided to the script via\n"); log("the standard $argc and $argv variables.\n"); log("\n"); + log("Note, tcl will not recieve the output of any yosys command. If the output\n"); + log("of the tcl commands are needed, use the yosys command 'tee' to redirect yosys's\n"); + log("output to a temporary file.\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *) override { if (args.size() < 2) @@ -867,6 +866,35 @@ std::string proc_self_dirname() { return "/"; } +#elif defined(__OpenBSD__) +char yosys_path[PATH_MAX]; +char *yosys_argv0; + +std::string proc_self_dirname(void) +{ + char buf[PATH_MAX + 1] = "", *path, *p; + // if case argv[0] contains a valid path, return it + if (strlen(yosys_path) > 0) { + p = strrchr(yosys_path, '/'); + snprintf(buf, sizeof buf, "%*s/", (int)(yosys_path - p), yosys_path); + return buf; + } + // if argv[0] does not, reconstruct the path out of $PATH + path = strdup(getenv("PATH")); + if (!path) + log_error("getenv(\"PATH\") failed: %s\n", strerror(errno)); + for (p = strtok(path, ":"); p; p = strtok(NULL, ":")) { + snprintf(buf, sizeof buf, "%s/%s", p, yosys_argv0); + if (access(buf, X_OK) == 0) { + *(strrchr(buf, '/') + 1) = '\0'; + free(path); + return buf; + } + } + free(path); + log_error("Can't determine yosys executable path\n."); + return NULL; +} #else #error "Don't know how to determine process executable base path!" #endif diff --git a/kernel/yosys.h b/kernel/yosys.h index 448f896d4..b5b1553f2 100644 --- a/kernel/yosys.h +++ b/kernel/yosys.h @@ -287,7 +287,7 @@ void remove_directory(std::string dirname); std::string escape_filename_spaces(const std::string& filename); template<typename T> int GetSize(const T &obj) { return obj.size(); } -int GetSize(RTLIL::Wire *wire); +inline int GetSize(RTLIL::Wire *wire); extern int autoidx; extern int yosys_xtrace; diff --git a/libs/fst/config.h b/libs/fst/config.h index cd036f16a..a2f0fca82 100644 --- a/libs/fst/config.h +++ b/libs/fst/config.h @@ -21,7 +21,7 @@ #undef HAVE_LIBPTHREAD #undef HAVE_FSEEKO #endif -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) #undef HAVE_ALLOCA_H #endif diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex index 3c9fb31cc..86b1f6a9a 100644 --- a/manual/CHAPTER_CellLib.tex +++ b/manual/CHAPTER_CellLib.tex @@ -603,7 +603,7 @@ Add information about {\tt \$specify2}, {\tt \$specify3}, and {\tt \$specrule} c \begin{fixme} Add information about {\tt \$assert}, {\tt \$assume}, {\tt \$live}, {\tt \$fair}, {\tt \$cover}, {\tt \$equiv}, -{\tt \$initstate}, {\tt \$anyconst}, {\tt \$anyseq}, {\tt \$allconst}, {\tt \$allseq} cells. +{\tt \$initstate}, {\tt \$anyconst}, {\tt \$anyseq}, {\tt \$anyinit}, {\tt \$allconst}, {\tt \$allseq} cells. \end{fixme} \begin{fixme} diff --git a/manual/command-reference-manual.tex b/manual/command-reference-manual.tex index edc8af6e6..bc25c35cd 100644 --- a/manual/command-reference-manual.tex +++ b/manual/command-reference-manual.tex @@ -2256,6 +2256,16 @@ and simulus signal from FST file number of clock cycles to simulate (default: 20) \end{lstlisting} +\section{gatemate\_foldinv -- fold inverters into Gatemate LUT trees} +\label{cmd:gatemate_foldinv} +\begin{lstlisting}[numbers=left,frame=single] + gatemate_foldinv [selection] + + +This pass searches for $__CC_NOT cells and folds them into CC_LUT2, CC_L2T4 +and CC_L2T5 cells as created by LUT tree mapping. +\end{lstlisting} + \section{glift -- create GLIFT models and optimization problems} \label{cmd:glift} \begin{lstlisting}[numbers=left,frame=single] @@ -2379,6 +2389,9 @@ resolves positional module parameters, unrolls array instances, and more. like -check, but also throw an error if blackbox modules are instantiated, and throw an error if the design has no top module. + -smtcheck + like -simcheck, but allow smtlib2_module modules. + -purge_lib by default the hierarchy command will not remove library (blackbox) modules. use this option to also remove unused blackbox modules. @@ -2597,7 +2610,7 @@ Tristate PADS (-toutpad, -tinoutpad) always operate in -bits mode. \begin{lstlisting}[numbers=left,frame=single] jny [options] [selection] -Write a JSON netlist metadata for the current design +Write JSON netlist metadata for the current design -o <filename> write to the specified file. @@ -2947,6 +2960,12 @@ pass to word-wide DFFs and address decoders. -iattr for -attr, ignore case of <value>. + + -rom-only + only perform conversion for ROMs (memories with no write ports). + + -keepdc + when mapping ROMs, keep x-bits shared across read ports. \end{lstlisting} \section{memory\_memx -- emulate vlog sim behavior for mem ports} @@ -3992,6 +4011,9 @@ Read cells from liberty file as modules into current design. -lib only create empty blackbox modules + -wb + mark imported cells as whiteboxes + -nooverwrite ignore re-definitions of modules. (the default behavior is to create an error message if the existing module is not a blackbox @@ -4223,10 +4245,12 @@ Assign names auto-generated from the src attribute to all selected wires and cells with private names. - rename -wire [selection] + rename -wire [selection] [-suffix <suffix>] Assign auto-generated names based on the wires they drive to all selected cells with private names. Ignores cells driving privatly named wires. +By default, the cell is named after the wire with the cell type as suffix. +The -suffix option can be used to set the suffix to the given string instead. rename -enumerate [-pattern <pattern>] [selection] @@ -5985,6 +6009,9 @@ This command runs synthesis for Cologne Chip AG GateMate FPGAs. -nomx8, -nomx4 do not use CC_MX{8,4} multiplexer cells in output netlist. + -luttree + use new LUT tree mapping approach (EXPERIMENTAL). + -dff run 'abc' with -dff option @@ -6064,7 +6091,11 @@ The following commands are executed by this synthesis command: techmap -map +/gatemate/mux_map.v map_luts: - abc -dress -lut 4 + abc -genlib +/gatemate/lut_tree_cells.genlib (with -luttree) + techmap -map +/gatemate/lut_tree_map.v (with -luttree) + gatemate_foldinv (with -luttree) + techmap -map +/gatemate/inv_map.v (with -luttree) + abc -dress -lut 4 (without -luttree) clean map_cells: @@ -7379,6 +7410,10 @@ in order to avoid a name collision with the built in commands. If any arguments are specified, these arguments are provided to the script via the standard $argc and $argv variables. + +Note, tcl will not recieve the output of any yosys command. If the output +of the tcl commands are needed, use the yosys command 'tee' to redirect yosys's +output to a temporary file. \end{lstlisting} \section{techmap -- generic technology mapper} @@ -7898,6 +7933,9 @@ Import options: -v, -vv Verbose log messages. (-vv is even more verbose than -v.) + -pp <filename> + Pretty print design after elaboration to specified file. + The following additional import options are useful for debugging the Verific bindings (for Yosys and/or Verific developers): @@ -7938,50 +7976,6 @@ Pretty print options: Save output for VHDL design units. - verific -app <application>.. - -Execute YosysHQ formal application on loaded Verilog files. - -Application options: - - -module <module> - Run formal application only on specified module. - - -blacklist <filename[:lineno]> - Do not run application on modules from files that match the filename - or filename and line number if provided in such format. - Parameter can also contain comma separated list of file locations. - - -blfile <file> - Do not run application on locations specified in file, they can - represent filename or filename and location in file. - -Applications: - - WARNING: Applications only available in commercial build. - - - verific -template <name> <top_module>.. - -Generate template for specified top module of loaded design. - -Template options: - - -out - Specifies output file for generated template, by default output is stdout - - -chparam name value - Generate template using this parameter value. Otherwise default parameter - values will be used for templat generate functionality. This option - can be specified multiple times to override multiple parameters. - String values must be passed in double quotes ("). - -Templates: - - WARNING: Templates only available in commercial build. - - - verific -cfg [<name> [<value>]] Get/set Verific runtime flags. @@ -8537,6 +8531,8 @@ http://bygone.clairexen.net/intersynth/ \begin{lstlisting}[numbers=left,frame=single] jny [options] [selection] +Write JSON netlist metadata for the current design + -no-connections Don't include connection information in the netlist output. @@ -8546,7 +8542,8 @@ http://bygone.clairexen.net/intersynth/ -no-properties Don't include property information in the netlist output. -Write a JSON metadata for the current design +The JSON schema for JNY output files is located in the "jny.schema.json" file +which is located at "https://raw.githubusercontent.com/YosysHQ/yosys/master/misc/jny.schema.json" \end{lstlisting} \section{write\_json -- write design to a JSON file} diff --git a/misc/jny.schema.json b/misc/jny.schema.json new file mode 100644 index 000000000..0fff8ee57 --- /dev/null +++ b/misc/jny.schema.json @@ -0,0 +1,193 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/YosysHQ/yosys/master/misc/jny.schema.json", + "title": "Yosys JSON Netlist metadata", + "description": "Yosys JSON Netlist", + "type": "object", + "properties": { + "generator": { + "type": "string", + "description": "JNY File Generator" + }, + "version": { + "type": "string", + "description": "JNY Version" + }, + "invocation": { + "type": "string", + "description": "Invocation line that generated the JNY metadata" + }, + "features": { + "type": "array", + "description": "What information is contained in the JNY file", + "items": { + "type": "string" + } + }, + "modules": { + "type": "array", + "items": { "$ref": "#/$defs/module" } + } + }, + "required": [ + "generator", + "version", + "invocation", + "features" + ], + "$defs": { + "module": { + "type": "object", + "description": "Module definition", + "properties": { + "name": { + "type": "string", + "description": "Module Name" + }, + "cell_sorts": { + "type": "array", + "description": "", + "items": { "$ref": "#/$defs/cell_sort" } + }, + "connections": { + "type": "array", + "description": "Cell connections", + "items": { "$ref": "#/$defs/connection" } + }, + "attributes": { + "type": "object", + "description": "Attributes attached to the module" + }, + "parameters": { + "type": "object", + "description": "Parameters attached to the module" + } + }, + "required": [ + "name", + "cell_sorts" + ] + }, + "cell_sort": { + "type": "object", + "description": "Describes a type of cell", + "properties": { + "type": { + "type": "string", + "description": "Type of cell" + }, + "ports": { + "type": "array", + "description": "Cell ports", + "items": { "$ref": "#/$defs/port" } + } + , + "cells": { + "type": "array", + "description": "Cells of cell_sort", + "items": { "$ref": "#/$defs/cell" } + } + }, + "required": [ + "type", + "ports", + "cells" + ] + }, + "connection": { + "type": "object", + "description": "A connection within a module or cell", + "properties": { + "name": { + "type": "string", + "description": "Connection name" + }, + "signals": { + "type": "array", + "description": "Signals that compose the connection", + "items": { "$ref": "#/$defs/signal" } + } + }, + "required": [ + "name", + "signals" + ] + }, + "port": { + "type": "object", + "description": "Cell port description", + "properties": { + "name": { + "type": "string", + "description": "Port name" + }, + "direction": { + "type": "string", + "description": "Port direction", + "enum": ["i", "o", "io", ""] + }, + "range": { + "type": "array", + "description": "Port width [MSB, LSB]", + "items": { + "type": "number" + }, + "minContains": 1, + "maxContains": 2 + } + }, + "required": [ + "name", + "direction", + "range" + ] + }, + "cell": { + "type": "object", + "description": "Module cell definition", + "properties": { + "name": { + "type": "string", + "description": "Cell name" + }, + "connections": { + "type": "array", + "description": "Cell connections", + "items": { "$ref": "#/$defs/connection" } + }, + "attributes": { + "type": "object", + "description": "Attributes attached to the cell" + }, + "parameters": { + "type": "object", + "description": "Parameters attached to the cell" + } + }, + "required": [ + "name" + ] + }, + "signal": { + "type": "object", + "description": "A signal definition", + "parameters": { + "width": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["wire", "chunk"] + }, + "const": { + "type": "boolean" + } + }, + "required": [ + "width", + "type", + "const" + ] + } + } +} diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 81da35ffe..45576c91c 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -20,6 +20,7 @@ #include "kernel/register.h" #include "kernel/rtlil.h" #include "kernel/log.h" +#include "kernel/hashlib.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -105,6 +106,60 @@ static IdString derive_name_from_cell_output_wire(const RTLIL::Cell *cell, strin return name + suffix; } +static bool rename_witness(RTLIL::Design *design, dict<RTLIL::Module *, int> &cache, RTLIL::Module *module) +{ + auto cached = cache.find(module); + if (cached != cache.end()) { + if (cached->second == -1) + log_error("Cannot rename witness signals in a design containing recursive instantiations.\n"); + return cached->second; + } + cache.emplace(module, -1); + + bool has_witness_signals = false; + for (auto cell : module->cells()) + { + RTLIL::Module *impl = design->module(cell->type); + if (impl != nullptr) { + bool witness_in_cell = rename_witness(design, cache, impl); + has_witness_signals |= witness_in_cell; + if (witness_in_cell && !cell->name.isPublic()) { + std::string name = cell->name.c_str() + 1; + for (auto &c : name) + if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') && c != '_') + c = '_'; + auto new_id = module->uniquify("\\_witness_." + name); + cell->set_hdlname_attribute({ "_witness_", strstr(new_id.c_str(), ".") + 1 }); + module->rename(cell, new_id); + } + } + + if (cell->type.in(ID($anyconst), ID($anyseq), ID($anyinit), ID($allconst), ID($allseq))) { + has_witness_signals = true; + auto QY = cell->type == ID($anyinit) ? ID::Q : ID::Y; + auto sig_out = cell->getPort(QY); + + for (auto chunk : sig_out.chunks()) { + if (chunk.is_wire() && !chunk.wire->name.isPublic()) { + std::string name = stringf("%s_%s", cell->type.c_str() + 1, cell->name.c_str() + 1); + for (auto &c : name) + if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') && c != '_') + c = '_'; + auto new_id = module->uniquify("\\_witness_." + name); + auto new_wire = module->addWire(new_id, GetSize(sig_out)); + new_wire->set_hdlname_attribute({ "_witness_", strstr(new_id.c_str(), ".") + 1 }); + module->connect({sig_out, new_wire}); + cell->setPort(QY, new_wire); + break; + } + } + } + } + + cache[module] = has_witness_signals; + return has_witness_signals; +} + struct RenamePass : public Pass { RenamePass() : Pass("rename", "rename object in the design") { } void help() override @@ -146,6 +201,14 @@ struct RenamePass : public Pass { log("pattern is '_%%_'.\n"); log("\n"); log("\n"); + log(" rename -witness\n"); + log("\n"); + log("Assigns auto-generated names to all $any*/$all* output wires and containing\n"); + log("cells that do not have a public name. This ensures that, during formal\n"); + log("verification, a solver-found trace can be fully specified using a public\n"); + log("hierarchical names.\n"); + log("\n"); + log("\n"); log(" rename -hide [selection]\n"); log("\n"); log("Assign private names (the ones with $-prefix) to all selected wires and cells\n"); @@ -156,6 +219,13 @@ struct RenamePass : public Pass { log("\n"); log("Rename top module.\n"); log("\n"); + log("\n"); + log(" rename -scramble-name [-seed <seed>] [selection]\n"); + log("\n"); + log("Assign randomly-generated names to all selected wires and cells. The seed option\n"); + log("can be used to change the random number generator seed from the default, but it\n"); + log("must be non-zero.\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) override { @@ -164,10 +234,13 @@ struct RenamePass : public Pass { bool flag_src = false; bool flag_wire = false; bool flag_enumerate = false; + bool flag_witness = false; bool flag_hide = false; bool flag_top = false; bool flag_output = false; + bool flag_scramble_name = false; bool got_mode = false; + unsigned int seed = 1; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -193,6 +266,11 @@ struct RenamePass : public Pass { got_mode = true; continue; } + if (arg == "-witness" && !got_mode) { + flag_witness = true; + got_mode = true; + continue; + } if (arg == "-hide" && !got_mode) { flag_hide = true; got_mode = true; @@ -203,6 +281,11 @@ struct RenamePass : public Pass { got_mode = true; continue; } + if (arg == "-scramble-name" && !got_mode) { + flag_scramble_name = true; + got_mode = true; + continue; + } if (arg == "-pattern" && argidx+1 < args.size() && args[argidx+1].find('%') != std::string::npos) { int pos = args[++argidx].find('%'); pattern_prefix = args[argidx].substr(0, pos); @@ -211,6 +294,11 @@ struct RenamePass : public Pass { } if (arg == "-suffix" && argidx + 1 < args.size()) { cell_suffix = args[++argidx]; + continue; + } + if (arg == "-seed" && argidx+1 < args.size()) { + seed = std::stoi(args[++argidx]); + continue; } break; } @@ -289,6 +377,19 @@ struct RenamePass : public Pass { } } else + if (flag_witness) + { + extra_args(args, argidx, design, false); + + RTLIL::Module *module = design->top_module(); + + if (module == nullptr) + log_cmd_error("No top module found!\n"); + + dict<RTLIL::Module *, int> cache; + rename_witness(design, cache, module); + } + else if (flag_hide) { extra_args(args, argidx, design); @@ -329,6 +430,42 @@ struct RenamePass : public Pass { design->rename(module, new_name); } else + if (flag_scramble_name) + { + extra_args(args, argidx, design); + + if (seed == 0) + log_error("Seed for -scramble-name cannot be zero.\n"); + + for (auto module : design->selected_modules()) + { + if (module->memories.size() != 0 || module->processes.size() != 0) { + log_warning("Skipping module %s with unprocessed memories or processes\n", log_id(module)); + continue; + } + + dict<RTLIL::Wire *, IdString> new_wire_names; + dict<RTLIL::Cell *, IdString> new_cell_names; + + for (auto wire : module->selected_wires()) + if (wire->port_id == 0) { + seed = mkhash_xorshift(seed); + new_wire_names[wire] = stringf("$_%u_", seed); + } + + for (auto cell : module->selected_cells()) { + seed = mkhash_xorshift(seed); + new_cell_names[cell] = stringf("$_%u_", seed); + } + + for (auto &it : new_wire_names) + module->rename(it.first, it.second); + + for (auto &it : new_cell_names) + module->rename(it.first, it.second); + } + } + else { if (argidx+2 != args.size()) log_cmd_error("Invalid number of arguments!\n"); diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc index a078b0b1c..590a7eb1d 100644 --- a/passes/cmds/setundef.cc +++ b/passes/cmds/setundef.cc @@ -20,6 +20,7 @@ #include "kernel/register.h" #include "kernel/celltypes.h" #include "kernel/sigtools.h" +#include "kernel/mem.h" #include "kernel/rtlil.h" #include "kernel/log.h" @@ -478,6 +479,29 @@ struct SetundefPass : public Pass { log_assert(ffbits.empty()); } + if (worker.next_bit_mode == MODE_ANYSEQ || worker.next_bit_mode == MODE_ANYCONST) + { + // Do not add anyseq / anyconst to unused memory port clocks + std::vector<Mem> memories = Mem::get_selected_memories(module); + for (auto &mem : memories) { + bool changed = false; + for (auto &rd_port : mem.rd_ports) { + if (!rd_port.clk_enable && rd_port.clk.is_fully_undef()) { + changed = true; + rd_port.clk = State::S0; + } + } + for (auto &wr_port : mem.rd_ports) { + if (!wr_port.clk_enable && wr_port.clk.is_fully_undef()) { + changed = true; + wr_port.clk = State::S0; + } + } + if (changed) + mem.emit(); + } + } + module->rewrite_sigspecs(worker); if (worker.next_bit_mode == MODE_ANYSEQ || worker.next_bit_mode == MODE_ANYCONST) diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 43deba47b..4d5605932 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -574,6 +574,7 @@ struct ShowWorker { ct.setup_internals(); ct.setup_internals_mem(); + ct.setup_internals_anyinit(); ct.setup_stdcells(); ct.setup_stdcells_mem(); ct.setup_design(design); diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index c858c8631..a4984597d 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -17,10 +17,13 @@ * */ +#include <iterator> + #include "kernel/yosys.h" #include "kernel/celltypes.h" #include "passes/techmap/libparse.h" #include "kernel/cost.h" +#include "libs/json11/json11.hpp" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -32,14 +35,14 @@ struct statdata_t #define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area) - #define X(_name) int _name; + #define X(_name) unsigned int _name; STAT_INT_MEMBERS #undef X double area; string tech; std::map<RTLIL::IdString, int> techinfo; - std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type; + std::map<RTLIL::IdString, unsigned int, RTLIL::sort_by_id_str> num_cells_by_type; std::set<RTLIL::IdString> unknown_cell_area; statdata_t operator+(const statdata_t &other) const @@ -53,7 +56,7 @@ struct statdata_t return sum; } - statdata_t operator*(int other) const + statdata_t operator*(unsigned int other) const { statdata_t sum = *this; #define X(_name) sum._name *= other; @@ -148,17 +151,17 @@ struct statdata_t void log_data(RTLIL::IdString mod_name, bool top_mod) { - log(" Number of wires: %6d\n", num_wires); - log(" Number of wire bits: %6d\n", num_wire_bits); - log(" Number of public wires: %6d\n", num_pub_wires); - log(" Number of public wire bits: %6d\n", num_pub_wire_bits); - log(" Number of memories: %6d\n", num_memories); - log(" Number of memory bits: %6d\n", num_memory_bits); - log(" Number of processes: %6d\n", num_processes); - log(" Number of cells: %6d\n", num_cells); + log(" Number of wires: %6u\n", num_wires); + log(" Number of wire bits: %6u\n", num_wire_bits); + log(" Number of public wires: %6u\n", num_pub_wires); + log(" Number of public wire bits: %6u\n", num_pub_wire_bits); + log(" Number of memories: %6u\n", num_memories); + log(" Number of memory bits: %6u\n", num_memory_bits); + log(" Number of processes: %6u\n", num_processes); + log(" Number of cells: %6u\n", num_cells); for (auto &it : num_cells_by_type) if (it.second) - log(" %-26s %6d\n", log_id(it.first), it.second); + log(" %-26s %6u\n", log_id(it.first), it.second); if (!unknown_cell_area.empty()) { log("\n"); @@ -173,13 +176,13 @@ struct statdata_t if (tech == "xilinx") { - int lut6_cnt = num_cells_by_type[ID(LUT6)]; - int lut5_cnt = num_cells_by_type[ID(LUT5)]; - int lut4_cnt = num_cells_by_type[ID(LUT4)]; - int lut3_cnt = num_cells_by_type[ID(LUT3)]; - int lut2_cnt = num_cells_by_type[ID(LUT2)]; - int lut1_cnt = num_cells_by_type[ID(LUT1)]; - int lc_cnt = 0; + unsigned int lut6_cnt = num_cells_by_type[ID(LUT6)]; + unsigned int lut5_cnt = num_cells_by_type[ID(LUT5)]; + unsigned int lut4_cnt = num_cells_by_type[ID(LUT4)]; + unsigned int lut3_cnt = num_cells_by_type[ID(LUT3)]; + unsigned int lut2_cnt = num_cells_by_type[ID(LUT2)]; + unsigned int lut1_cnt = num_cells_by_type[ID(LUT1)]; + unsigned int lc_cnt = 0; lc_cnt += lut6_cnt; @@ -221,12 +224,12 @@ struct statdata_t lc_cnt += (lut2_cnt + lut1_cnt + 1) / 2; log("\n"); - log(" Estimated number of LCs: %10d\n", lc_cnt); + log(" Estimated number of LCs: %10u\n", lc_cnt); } if (tech == "cmos") { - int tran_cnt = 0; + unsigned int tran_cnt = 0; bool tran_cnt_exact = true; auto &gate_costs = CellCosts::cmos_gate_cost(); @@ -243,20 +246,48 @@ struct statdata_t } log("\n"); - log(" Estimated number of transistors: %10d%s\n", tran_cnt, tran_cnt_exact ? "" : "+"); + log(" Estimated number of transistors: %10u%s\n", tran_cnt, tran_cnt_exact ? "" : "+"); } } + + void log_data_json(const char *mod_name, bool first_module) + { + if (!first_module) + log(",\n"); + log(" %s: {\n", json11::Json(mod_name).dump().c_str()); + log(" \"num_wires\": %u,\n", num_wires); + log(" \"num_wire_bits\": %u,\n", num_wire_bits); + log(" \"num_pub_wires\": %u,\n", num_pub_wires); + log(" \"num_pub_wire_bits\": %u,\n", num_pub_wire_bits); + log(" \"num_memories\": %u,\n", num_memories); + log(" \"num_memory_bits\": %u,\n", num_memory_bits); + log(" \"num_processes\": %u,\n", num_processes); + log(" \"num_cells\": %u,\n", num_cells); + log(" \"num_cells_by_type\": {\n"); + bool first_line = true; + for (auto &it : num_cells_by_type) + if (it.second) { + if (!first_line) + log(",\n"); + log(" %s: %u", json11::Json(log_id(it.first)).dump().c_str(), it.second); + first_line = false; + } + log("\n"); + log(" }\n"); + log(" }"); + } }; -statdata_t hierarchy_worker(std::map<RTLIL::IdString, statdata_t> &mod_stat, RTLIL::IdString mod, int level) +statdata_t hierarchy_worker(std::map<RTLIL::IdString, statdata_t> &mod_stat, RTLIL::IdString mod, int level, bool quiet = false) { statdata_t mod_data = mod_stat.at(mod); - std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type; + std::map<RTLIL::IdString, unsigned int, RTLIL::sort_by_id_str> num_cells_by_type; num_cells_by_type.swap(mod_data.num_cells_by_type); for (auto &it : num_cells_by_type) if (mod_stat.count(it.first) > 0) { - log(" %*s%-*s %6d\n", 2*level, "", 26-2*level, log_id(it.first), it.second); + if (!quiet) + log(" %*s%-*s %6u\n", 2*level, "", 26-2*level, log_id(it.first), it.second); mod_data = mod_data + hierarchy_worker(mod_stat, it.first, level+1) * it.second; mod_data.num_cells -= it.second; } else { @@ -314,12 +345,16 @@ struct StatPass : public Pass { log(" annotate internal cell types with their word width.\n"); log(" e.g. $add_8 for an 8 bit wide $add cell.\n"); log("\n"); + log(" -json\n"); + log(" output the statistics in a machine-readable JSON format.\n"); + log(" this is output to the console; use \"tee\" to output to a file.\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Printing statistics.\n"); - bool width_mode = false; + bool width_mode = false, json_mode = false; RTLIL::Module *top_mod = nullptr; std::map<RTLIL::IdString, statdata_t> mod_stat; dict<IdString, double> cell_area; @@ -348,13 +383,27 @@ struct StatPass : public Pass { top_mod = design->module(RTLIL::escape_id(args[++argidx])); continue; } + if (args[argidx] == "-json") { + json_mode = true; + continue; + } break; } extra_args(args, argidx, design); - if (techname != "" && techname != "xilinx" && techname != "cmos") + if (techname != "" && techname != "xilinx" && techname != "cmos" && !json_mode) log_cmd_error("Unsupported technology: '%s'\n", techname.c_str()); + if (json_mode) { + log("{\n"); + log(" \"creator\": %s,\n", json11::Json(yosys_version_str).dump().c_str()); + std::stringstream invocation; + std::copy(args.begin(), args.end(), std::ostream_iterator<std::string>(invocation, " ")); + log(" \"invocation\": %s,\n", json11::Json(invocation.str()).dump().c_str()); + log(" \"modules\": {\n"); + } + + bool first_module = true; for (auto mod : design->selected_modules()) { if (!top_mod && design->full_selection()) @@ -364,23 +413,40 @@ struct StatPass : public Pass { statdata_t data(design, mod, width_mode, cell_area, techname); mod_stat[mod->name] = data; + if (json_mode) { + data.log_data_json(mod->name.c_str(), first_module); + first_module = false; + } else { + log("\n"); + log("=== %s%s ===\n", log_id(mod->name), design->selected_whole_module(mod->name) ? "" : " (partially selected)"); + log("\n"); + data.log_data(mod->name, false); + } + } + + if (json_mode) { log("\n"); - log("=== %s%s ===\n", log_id(mod->name), design->selected_whole_module(mod->name) ? "" : " (partially selected)"); - log("\n"); - data.log_data(mod->name, false); + log(" },\n"); } - if (top_mod != nullptr && GetSize(mod_stat) > 1) + if (top_mod != nullptr) { - log("\n"); - log("=== design hierarchy ===\n"); - log("\n"); + if (!json_mode && GetSize(mod_stat) > 1) { + log("\n"); + log("=== design hierarchy ===\n"); + log("\n"); + log(" %-28s %6d\n", log_id(top_mod->name), 1); + } - log(" %-28s %6d\n", log_id(top_mod->name), 1); - statdata_t data = hierarchy_worker(mod_stat, top_mod->name, 0); + statdata_t data = hierarchy_worker(mod_stat, top_mod->name, 0, /*quiet=*/json_mode); + + if (json_mode) + data.log_data_json("design", true); + else if (GetSize(mod_stat) > 1) { + log("\n"); + data.log_data(top_mod->name, true); + } - log("\n"); - data.log_data(top_mod->name, true); design->scratchpad_set_int("stat.num_wires", data.num_wires); design->scratchpad_set_int("stat.num_wire_bits", data.num_wire_bits); design->scratchpad_set_int("stat.num_pub_wires", data.num_pub_wires); @@ -392,6 +458,11 @@ struct StatPass : public Pass { design->scratchpad_set_int("stat.area", data.area); } + if (json_mode) { + log("\n"); + log("}\n"); + } + log("\n"); } } StatPass; diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc index a2d38a0bd..f829714c4 100644 --- a/passes/fsm/fsm_detect.cc +++ b/passes/fsm/fsm_detect.cc @@ -280,6 +280,7 @@ struct FsmDetectPass : public Pass { CellTypes ct; ct.setup_internals(); + ct.setup_internals_anyinit(); ct.setup_internals_mem(); ct.setup_stdcells(); ct.setup_stdcells_mem(); diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index d40d6e59f..d27fddf1c 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -439,7 +439,8 @@ void check_cell_connections(const RTLIL::Module &module, RTLIL::Cell &cell, RTLI } } -bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector<std::string> &libdirs) +bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, bool flag_smtcheck, + std::vector<std::string> &libdirs) { bool did_something = false; std::map<RTLIL::Cell*, std::pair<int, int>> array_cells; @@ -477,7 +478,7 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check RTLIL::Module *mod = design->module(cell->type); if (!mod) { - mod = get_module(*design, *cell, *module, flag_check || flag_simcheck, libdirs); + mod = get_module(*design, *cell, *module, flag_check || flag_simcheck || flag_smtcheck, libdirs); // If we still don't have a module, treat the cell as a black box and skip // it. Otherwise, we either loaded or derived something so should set the @@ -495,11 +496,11 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check // interfaces. if_expander.visit_connections(*cell, *mod); - if (flag_check || flag_simcheck) + if (flag_check || flag_simcheck || flag_smtcheck) check_cell_connections(*module, *cell, *mod); if (mod->get_blackbox_attribute()) { - if (flag_simcheck) + if (flag_simcheck || (flag_smtcheck && !mod->get_bool_attribute(ID::smtlib2_module))) log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox/whitebox module.\n", cell->type.c_str(), module->name.c_str(), cell->name.c_str()); continue; @@ -737,6 +738,9 @@ struct HierarchyPass : public Pass { log(" like -check, but also throw an error if blackbox modules are\n"); log(" instantiated, and throw an error if the design has no top module.\n"); log("\n"); + log(" -smtcheck\n"); + log(" like -simcheck, but allow smtlib2_module modules.\n"); + log("\n"); log(" -purge_lib\n"); log(" by default the hierarchy command will not remove library (blackbox)\n"); log(" modules. use this option to also remove unused blackbox modules.\n"); @@ -803,6 +807,7 @@ struct HierarchyPass : public Pass { bool flag_check = false; bool flag_simcheck = false; + bool flag_smtcheck = false; bool purge_lib = false; RTLIL::Module *top_mod = NULL; std::string load_top_mod; @@ -821,7 +826,7 @@ struct HierarchyPass : public Pass { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-generate" && !flag_check && !flag_simcheck && !top_mod) { + if (args[argidx] == "-generate" && !flag_check && !flag_simcheck && !flag_smtcheck && !top_mod) { generate_mode = true; log("Entering generate mode.\n"); while (++argidx < args.size()) { @@ -868,6 +873,10 @@ struct HierarchyPass : public Pass { flag_simcheck = true; continue; } + if (args[argidx] == "-smtcheck") { + flag_smtcheck = true; + continue; + } if (args[argidx] == "-purge_lib") { purge_lib = true; continue; @@ -1013,7 +1022,7 @@ struct HierarchyPass : public Pass { } } - if (flag_simcheck && top_mod == nullptr) + if ((flag_simcheck || flag_smtcheck) && top_mod == nullptr) log_error("Design has no top module.\n"); if (top_mod != NULL) { @@ -1039,7 +1048,7 @@ struct HierarchyPass : public Pass { } for (auto module : used_modules) { - if (expand_module(design, module, flag_check, flag_simcheck, libdirs)) + if (expand_module(design, module, flag_check, flag_simcheck, flag_smtcheck, libdirs)) did_something = true; } diff --git a/passes/hierarchy/submod.cc b/passes/hierarchy/submod.cc index 845dc850f..c0c40671d 100644 --- a/passes/hierarchy/submod.cc +++ b/passes/hierarchy/submod.cc @@ -260,6 +260,7 @@ struct SubmodWorker } ct.setup_internals(); + ct.setup_internals_anyinit(); ct.setup_internals_mem(); ct.setup_stdcells(); ct.setup_stdcells_mem(); diff --git a/passes/memory/memory_libmap.cc b/passes/memory/memory_libmap.cc index 898e0af85..9e147b0bf 100644 --- a/passes/memory/memory_libmap.cc +++ b/passes/memory/memory_libmap.cc @@ -837,7 +837,7 @@ void MemMapping::handle_priority() { if (!port2.priority_mask[p1idx]) continue; for (auto &cfg: cfgs) { - auto &p1cfg = cfg.rd_ports[p1idx]; + auto &p1cfg = cfg.wr_ports[p1idx]; auto &p2cfg = cfg.wr_ports[p2idx]; bool found = false; for (auto &pgi: p2cfg.def->wrprio) { @@ -1706,10 +1706,11 @@ void MemMapping::emit_port(const MemConfig &cfg, std::vector<Cell*> &cells, cons if (pdef.wrbe_separate) { cell->setPort(stringf("\\PORT_%s_WR_EN", name), State::S0); cell->setPort(stringf("\\PORT_%s_WR_BE", name), hw_wren); - cell->setParam(stringf("\\PORT_%s_WR_BE_WIDTH", name), GetSize(hw_wren)); + if (cfg.def->width_mode != WidthMode::Single) + cell->setParam(stringf("\\PORT_%s_WR_BE_WIDTH", name), GetSize(hw_wren)); } else { cell->setPort(stringf("\\PORT_%s_WR_EN", name), hw_wren); - if (cfg.def->byte != 0) + if (cfg.def->byte != 0 && cfg.def->width_mode != WidthMode::Single) cell->setParam(stringf("\\PORT_%s_WR_EN_WIDTH", name), GetSize(hw_wren)); } } diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc index ca1ca483d..e2f74c2e1 100644 --- a/passes/memory/memory_map.cc +++ b/passes/memory/memory_map.cc @@ -30,6 +30,9 @@ PRIVATE_NAMESPACE_BEGIN struct MemoryMapWorker { bool attr_icase = false; + bool rom_only = false; + bool keepdc = false; + bool formal = false; dict<RTLIL::IdString, std::vector<RTLIL::Const>> attributes; RTLIL::Design *design; @@ -107,11 +110,8 @@ struct MemoryMapWorker SigSpec init_data = mem.get_init_data(); - // delete unused memory cell - if (mem.rd_ports.empty()) { - mem.remove(); + if (!mem.wr_ports.empty() && rom_only) return; - } // check if attributes allow us to infer FFRAM for this memory for (const auto &attr : attributes) { @@ -143,9 +143,17 @@ struct MemoryMapWorker } } + // delete unused memory cell + if (mem.rd_ports.empty()) { + mem.remove(); + return; + } + // all write ports must share the same clock RTLIL::SigSpec refclock; bool refclock_pol = false; + bool async_wr = false; + bool static_only = true; for (int i = 0; i < GetSize(mem.wr_ports); i++) { auto &port = mem.wr_ports[i]; if (port.en.is_fully_const() && !port.en.as_bool()) { @@ -159,10 +167,20 @@ struct MemoryMapWorker static_ports.insert(i); continue; } - log("Not mapping memory %s in module %s (write port %d has no clock).\n", - mem.memid.c_str(), module->name.c_str(), i); - return; + static_only = false; + if (GetSize(refclock) != 0) + log("Not mapping memory %s in module %s (mixed clocked and async write ports).\n", + mem.memid.c_str(), module->name.c_str()); + if (!formal) + log("Not mapping memory %s in module %s (write port %d has no clock).\n", + mem.memid.c_str(), module->name.c_str(), i); + async_wr = true; + continue; } + static_only = false; + if (async_wr) + log("Not mapping memory %s in module %s (mixed clocked and async write ports).\n", + mem.memid.c_str(), module->name.c_str()); if (refclock.size() == 0) { refclock = port.clk; refclock_pol = port.clk_polarity; @@ -180,28 +198,47 @@ struct MemoryMapWorker std::vector<RTLIL::SigSpec> data_reg_in(1 << abits); std::vector<RTLIL::SigSpec> data_reg_out(1 << abits); + std::vector<RTLIL::SigSpec> &data_read = async_wr ? data_reg_in : data_reg_out; + int count_static = 0; for (int i = 0; i < mem.size; i++) { int addr = i + mem.start_offset; int idx = addr & ((1 << abits) - 1); + SigSpec w_init = init_data.extract(i*mem.width, mem.width); if (static_cells_map.count(addr) > 0) { - data_reg_out[idx] = static_cells_map[addr]; + data_read[idx] = static_cells_map[addr]; count_static++; } + else if (static_only && (!keepdc || w_init.is_fully_def())) + { + data_read[idx] = w_init; + } else { - RTLIL::Cell *c = module->addCell(genid(mem.memid, "", addr), ID($dff)); - c->parameters[ID::WIDTH] = mem.width; - if (GetSize(refclock) != 0) { + RTLIL::Cell *c; + auto ff_id = genid(mem.memid, "", addr); + + if (static_only) { + // non-static part is a ROM, we only reach this with keepdc + if (formal) { + c = module->addCell(ff_id, ID($ff)); + } else { + c = module->addCell(ff_id, ID($dff)); + c->parameters[ID::CLK_POLARITY] = RTLIL::Const(RTLIL::State::S1); + c->setPort(ID::CLK, RTLIL::SigSpec(RTLIL::State::S0)); + } + } else if (async_wr) { + log_assert(formal); // General async write not implemented yet, checked against above + c = module->addCell(ff_id, ID($ff)); + } else { + c = module->addCell(ff_id, ID($dff)); c->parameters[ID::CLK_POLARITY] = RTLIL::Const(refclock_pol); c->setPort(ID::CLK, refclock); - } else { - c->parameters[ID::CLK_POLARITY] = RTLIL::Const(RTLIL::State::S1); - c->setPort(ID::CLK, RTLIL::SigSpec(RTLIL::State::S0)); } + c->parameters[ID::WIDTH] = mem.width; RTLIL::Wire *w_in = module->addWire(genid(mem.memid, "", addr, "$d"), mem.width); data_reg_in[idx] = w_in; @@ -212,17 +249,28 @@ struct MemoryMapWorker w_out_name = genid(mem.memid, "", addr, "$q"); RTLIL::Wire *w_out = module->addWire(w_out_name, mem.width); - SigSpec w_init = init_data.extract(i*mem.width, mem.width); + + if (formal && mem.packed && mem.cell->name.c_str()[0] == '\\') { + auto hdlname = mem.cell->get_hdlname_attribute(); + if (hdlname.empty()) + hdlname.push_back(mem.cell->name.c_str() + 1); + hdlname.push_back(stringf("[%d]", addr)); + w_out->set_hdlname_attribute(hdlname); + } if (!w_init.is_fully_undef()) w_out->attributes[ID::init] = w_init.as_const(); data_reg_out[idx] = w_out; c->setPort(ID::Q, w_out); + + if (static_only) + module->connect(RTLIL::SigSig(w_in, w_out)); } } - log(" created %d $dff cells and %d static cells of width %d.\n", mem.size-count_static, count_static, mem.width); + log(" created %d %s cells and %d static cells of width %d.\n", + mem.size-count_static, formal && (static_only || async_wr) ? "$ff" : "$dff", count_static, mem.width); int count_dff = 0, count_mux = 0, count_wrmux = 0; @@ -260,75 +308,78 @@ struct MemoryMapWorker } for (int j = 0; j < (1 << abits); j++) - if (data_reg_out[j] != SigSpec()) - module->connect(RTLIL::SigSig(rd_signals[j >> port.wide_log2].extract((j & ((1 << port.wide_log2) - 1)) * mem.width, mem.width), data_reg_out[j])); + if (data_read[j] != SigSpec()) + module->connect(RTLIL::SigSig(rd_signals[j >> port.wide_log2].extract((j & ((1 << port.wide_log2) - 1)) * mem.width, mem.width), data_read[j])); } log(" read interface: %d $dff and %d $mux cells.\n", count_dff, count_mux); - for (int i = 0; i < mem.size; i++) + if (!static_only) { - int addr = i + mem.start_offset; - int idx = addr & ((1 << abits) - 1); - if (static_cells_map.count(addr) > 0) - continue; - - RTLIL::SigSpec sig = data_reg_out[idx]; - - for (int j = 0; j < GetSize(mem.wr_ports); j++) + for (int i = 0; i < mem.size; i++) { - auto &port = mem.wr_ports[j]; - RTLIL::SigSpec wr_addr = port.addr.extract_end(port.wide_log2); - RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(addr >> port.wide_log2, GetSize(wr_addr))); + int addr = i + mem.start_offset; + int idx = addr & ((1 << abits) - 1); + if (static_cells_map.count(addr) > 0) + continue; - int sub = addr & ((1 << port.wide_log2) - 1); + RTLIL::SigSpec sig = data_reg_out[idx]; - int wr_offset = 0; - while (wr_offset < mem.width) + for (int j = 0; j < GetSize(mem.wr_ports); j++) { - int wr_width = 1; - RTLIL::SigSpec wr_bit = port.en.extract(wr_offset + sub * mem.width, 1); - - while (wr_offset + wr_width < mem.width) { - RTLIL::SigSpec next_wr_bit = port.en.extract(wr_offset + wr_width + sub * mem.width, 1); - if (next_wr_bit != wr_bit) - break; - wr_width++; - } + auto &port = mem.wr_ports[j]; + RTLIL::SigSpec wr_addr = port.addr.extract_end(port.wide_log2); + RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(addr >> port.wide_log2, GetSize(wr_addr))); - RTLIL::Wire *w = w_seladdr; + int sub = addr & ((1 << port.wide_log2) - 1); - if (wr_bit != State::S1) + int wr_offset = 0; + while (wr_offset < mem.width) { - RTLIL::Cell *c = module->addCell(genid(mem.memid, "$wren", addr, "", j, "", wr_offset), ID($and)); - c->parameters[ID::A_SIGNED] = RTLIL::Const(0); - c->parameters[ID::B_SIGNED] = RTLIL::Const(0); - c->parameters[ID::A_WIDTH] = RTLIL::Const(1); - c->parameters[ID::B_WIDTH] = RTLIL::Const(1); - c->parameters[ID::Y_WIDTH] = RTLIL::Const(1); - c->setPort(ID::A, w); - c->setPort(ID::B, wr_bit); - - w = module->addWire(genid(mem.memid, "$wren", addr, "", j, "", wr_offset, "$y")); - c->setPort(ID::Y, RTLIL::SigSpec(w)); + int wr_width = 1; + RTLIL::SigSpec wr_bit = port.en.extract(wr_offset + sub * mem.width, 1); + + while (wr_offset + wr_width < mem.width) { + RTLIL::SigSpec next_wr_bit = port.en.extract(wr_offset + wr_width + sub * mem.width, 1); + if (next_wr_bit != wr_bit) + break; + wr_width++; + } + + RTLIL::Wire *w = w_seladdr; + + if (wr_bit != State::S1) + { + RTLIL::Cell *c = module->addCell(genid(mem.memid, "$wren", addr, "", j, "", wr_offset), ID($and)); + c->parameters[ID::A_SIGNED] = RTLIL::Const(0); + c->parameters[ID::B_SIGNED] = RTLIL::Const(0); + c->parameters[ID::A_WIDTH] = RTLIL::Const(1); + c->parameters[ID::B_WIDTH] = RTLIL::Const(1); + c->parameters[ID::Y_WIDTH] = RTLIL::Const(1); + c->setPort(ID::A, w); + c->setPort(ID::B, wr_bit); + + w = module->addWire(genid(mem.memid, "$wren", addr, "", j, "", wr_offset, "$y")); + c->setPort(ID::Y, RTLIL::SigSpec(w)); + } + + RTLIL::Cell *c = module->addCell(genid(mem.memid, "$wrmux", addr, "", j, "", wr_offset), ID($mux)); + c->parameters[ID::WIDTH] = wr_width; + c->setPort(ID::A, sig.extract(wr_offset, wr_width)); + c->setPort(ID::B, port.data.extract(wr_offset + sub * mem.width, wr_width)); + c->setPort(ID::S, RTLIL::SigSpec(w)); + + w = module->addWire(genid(mem.memid, "$wrmux", addr, "", j, "", wr_offset, "$y"), wr_width); + c->setPort(ID::Y, w); + + sig.replace(wr_offset, w); + wr_offset += wr_width; + count_wrmux++; } - - RTLIL::Cell *c = module->addCell(genid(mem.memid, "$wrmux", addr, "", j, "", wr_offset), ID($mux)); - c->parameters[ID::WIDTH] = wr_width; - c->setPort(ID::A, sig.extract(wr_offset, wr_width)); - c->setPort(ID::B, port.data.extract(wr_offset + sub * mem.width, wr_width)); - c->setPort(ID::S, RTLIL::SigSpec(w)); - - w = module->addWire(genid(mem.memid, "$wrmux", addr, "", j, "", wr_offset, "$y"), wr_width); - c->setPort(ID::Y, w); - - sig.replace(wr_offset, w); - wr_offset += wr_width; - count_wrmux++; } - } - module->connect(RTLIL::SigSig(data_reg_in[idx], sig)); + module->connect(RTLIL::SigSig(data_reg_in[idx], sig)); + } } log(" write interface: %d write mux blocks.\n", count_wrmux); @@ -366,10 +417,25 @@ struct MemoryMapPass : public Pass { log(" -iattr\n"); log(" for -attr, ignore case of <value>.\n"); log("\n"); + log(" -rom-only\n"); + log(" only perform conversion for ROMs (memories with no write ports).\n"); + log("\n"); + log(" -keepdc\n"); + log(" when mapping ROMs, keep x-bits shared across read ports.\n"); + log("\n"); + log(" -formal\n"); + log(" map memories for a global clock based formal verification flow.\n"); + log(" This implies -keepdc, uses $ff cells for ROMs and sets hdlname\n"); + log(" attributes. It also has limited support for async write ports\n"); + log(" as generated by clk2fflogic.\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) override { bool attr_icase = false; + bool rom_only = false; + bool keepdc = false; + bool formal = false; dict<RTLIL::IdString, std::vector<RTLIL::Const>> attributes; log_header(design, "Executing MEMORY_MAP pass (converting memories to logic and flip-flops).\n"); @@ -406,6 +472,22 @@ struct MemoryMapPass : public Pass { attr_icase = true; continue; } + if (args[argidx] == "-rom-only") + { + rom_only = true; + continue; + } + if (args[argidx] == "-keepdc") + { + keepdc = true; + continue; + } + if (args[argidx] == "-formal") + { + formal = true; + keepdc = true; + continue; + } break; } extra_args(args, argidx, design); @@ -414,6 +496,9 @@ struct MemoryMapPass : public Pass { MemoryMapWorker worker(design, mod); worker.attr_icase = attr_icase; worker.attributes = attributes; + worker.rom_only = rom_only; + worker.keepdc = keepdc; + worker.formal = formal; worker.run(); } } diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index cb2c261c4..dde7c5299 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -633,6 +633,7 @@ struct OptCleanPass : public Pass { keep_cache.reset(design); ct_reg.setup_internals_mem(); + ct_reg.setup_internals_anyinit(); ct_reg.setup_stdcells_mem(); ct_all.setup(design); @@ -694,6 +695,7 @@ struct CleanPass : public Pass { keep_cache.reset(design); ct_reg.setup_internals_mem(); + ct_reg.setup_internals_anyinit(); ct_reg.setup_stdcells_mem(); ct_all.setup(design); diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index 0ad4acec2..6ff2d1b4b 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -491,12 +491,17 @@ struct OptDffWorker ff.has_srst = false; ff.sig_d = ff.val_srst; changed = true; - } else { + } else if (!opt.keepdc || ff.val_init.is_fully_def()) { log("Handling never-active EN on %s (%s) from module %s (removing D path).\n", log_id(cell), log_id(cell->type), log_id(module)); // The D input path is effectively useless, so remove it (this will be a D latch, SR latch, or a const driver). ff.has_ce = ff.has_clk = ff.has_srst = false; changed = true; + } else { + // We need to keep the undefined initival around as such + ff.sig_d = ff.sig_q; + ff.has_ce = ff.has_srst = false; + changed = true; } } else if (ff.sig_ce == (ff.pol_ce ? State::S1 : State::S0)) { // Always-active enable. Just remove it. @@ -508,13 +513,20 @@ struct OptDffWorker } } - if (ff.has_clk) { - if (ff.sig_clk.is_fully_const()) { + if (ff.has_clk && ff.sig_clk.is_fully_const()) { + if (!opt.keepdc || ff.val_init.is_fully_def()) { // Const clock — the D input path is effectively useless, so remove it (this will be a D latch, SR latch, or a const driver). log("Handling const CLK on %s (%s) from module %s (removing D path).\n", log_id(cell), log_id(cell->type), log_id(module)); ff.has_ce = ff.has_clk = ff.has_srst = false; changed = true; + } else { + // Const clock, but we need to keep the undefined initval around as such + if (ff.has_ce || ff.has_srst || ff.sig_d != ff.sig_q) { + ff.sig_d = ff.sig_q; + ff.has_ce = ff.has_srst = false; + changed = true; + } } } @@ -550,7 +562,7 @@ struct OptDffWorker ff.has_srst = false; ff.sig_d = ff.val_srst; changed = true; - } else { + } else if (!opt.keepdc || ff.val_init.is_fully_def()) { // The D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver). log("Handling D = Q on %s (%s) from module %s (removing D path).\n", log_id(cell), log_id(cell->type), log_id(module)); @@ -567,7 +579,7 @@ struct OptDffWorker } // The cell has been simplified as much as possible already. Now try to spice it up with enables / sync resets. - if (ff.has_clk) { + if (ff.has_clk && ff.sig_d != ff.sig_q) { if (!ff.has_arst && !ff.has_sr && (!ff.has_srst || !ff.has_ce || ff.ce_over_srst) && !opt.nosdff) { // Try to merge sync resets. std::map<ctrls_t, std::vector<int>> groups; diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc index 1a7c93fbd..c36a38dae 100644 --- a/passes/opt/opt_reduce.cc +++ b/passes/opt/opt_reduce.cc @@ -594,11 +594,9 @@ struct OptReduceWorker if (cell->type.in(ID($mux), ID($pmux))) opt_pmux(cell); - - if (cell->type == ID($bmux)) + else if (cell->type == ID($bmux)) opt_bmux(cell); - - if (cell->type == ID($demux)) + else if (cell->type == ID($demux)) opt_demux(cell); } } diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 08ab6de6f..8fd4c788c 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -166,8 +166,8 @@ struct WreduceWorker for (int i = GetSize(sig_q)-1; i >= 0; i--) { - if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || initval[i] == State::Sx) && - (!has_reset || i >= GetSize(rst_value) || rst_value[i] == State::S0 || rst_value[i] == State::Sx)) { + if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || (!config->keepdc && initval[i] == State::Sx)) && + (!has_reset || i >= GetSize(rst_value) || rst_value[i] == State::S0 || (!config->keepdc && rst_value[i] == State::Sx))) { module->connect(sig_q[i], State::S0); initvals.remove_init(sig_q[i]); sig_d.remove(i); @@ -175,8 +175,8 @@ struct WreduceWorker continue; } - if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1] && - (!has_reset || i >= GetSize(rst_value) || rst_value[i] == rst_value[i-1])) { + if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1] && (!config->keepdc || initval[i] != State::Sx) && + (!has_reset || i >= GetSize(rst_value) || (rst_value[i] == rst_value[i-1] && (!config->keepdc || rst_value[i] != State::Sx)))) { module->connect(sig_q[i], sig_q[i-1]); initvals.remove_init(sig_q[i]); sig_d.remove(i); diff --git a/passes/sat/Makefile.inc b/passes/sat/Makefile.inc index da6d49433..ebe3dc536 100644 --- a/passes/sat/Makefile.inc +++ b/passes/sat/Makefile.inc @@ -10,6 +10,7 @@ OBJS += passes/sat/expose.o OBJS += passes/sat/assertpmux.o OBJS += passes/sat/clk2fflogic.o OBJS += passes/sat/async2sync.o +OBJS += passes/sat/formalff.o OBJS += passes/sat/supercover.o OBJS += passes/sat/fmcombine.o OBJS += passes/sat/mutate.o diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc index 46c76eba9..6fdf470b1 100644 --- a/passes/sat/async2sync.cc +++ b/passes/sat/async2sync.cc @@ -75,6 +75,9 @@ struct Async2syncPass : public Pass { if (ff.has_gclk) continue; + if (ff.has_clk && ff.sig_clk.is_fully_const()) + ff.has_ce = ff.has_clk = ff.has_srst = false; + if (ff.has_clk) { if (ff.has_sr) { diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index b1b0567a0..2384ffced 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -233,7 +233,10 @@ struct Clk2fflogicPass : public Pass { qval = past_q; } - if (ff.has_aload) { + // The check for a constant sig_aload is also done by opt_dff, but when using verific and running + // clk2fflogic before opt_dff (which does more and possibly unwanted optimizations) this check avoids + // generating a lot of extra logic. + if (ff.has_aload && ff.sig_aload != (ff.pol_aload ? State::S0 : State::S1)) { SigSpec sig_aload = wrap_async_control(module, ff.sig_aload, ff.pol_aload, ff.is_fine, NEW_ID); if (!ff.is_fine) diff --git a/passes/sat/formalff.cc b/passes/sat/formalff.cc new file mode 100644 index 000000000..209486a37 --- /dev/null +++ b/passes/sat/formalff.cc @@ -0,0 +1,549 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2022 Jannis Harder <jix@yosyshq.com> <me@jix.one> + * + * 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/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/ffinit.h" +#include "kernel/ff.h" +#include "kernel/modtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + + +// Finds signal values with known constant or known unused values in the initial state +struct InitValWorker +{ + Module *module; + + ModWalker modwalker; + SigMap &sigmap; + FfInitVals initvals; + + dict<RTLIL::SigBit, RTLIL::State> initconst_bits; + dict<RTLIL::SigBit, bool> used_bits; + + InitValWorker(Module *module) : module(module), modwalker(module->design), sigmap(modwalker.sigmap) + { + modwalker.setup(module); + initvals.set(&modwalker.sigmap, module); + + for (auto wire : module->wires()) + if (wire->name.isPublic() || wire->get_bool_attribute(ID::keep)) + for (auto bit : SigSpec(wire)) + used_bits[sigmap(bit)] = true; + } + + // Sign/Zero-extended indexing of individual port bits + static SigBit bit_in_port(RTLIL::Cell *cell, RTLIL::IdString port, RTLIL::IdString sign, int index) + { + auto sig_port = cell->getPort(port); + if (index < GetSize(sig_port)) + return sig_port[index]; + else if (cell->getParam(sign).as_bool()) + return GetSize(sig_port) > 0 ? sig_port[GetSize(sig_port) - 1] : State::Sx; + else + return State::S0; + } + + // Has the signal a known constant value in the initial state? + // + // For sync-only FFs outputs, this is their initval. For logic loops, + // multiple drivers or unknown cells this is Sx. For a small number of + // handled cells we recurse through their inputs. All results are cached. + RTLIL::State initconst(SigBit bit) + { + sigmap.apply(bit); + + if (!bit.is_wire()) + return bit.data; + + auto it = initconst_bits.find(bit); + if (it != initconst_bits.end()) + return it->second; + + // Setting this temporarily to x takes care of any logic loops + initconst_bits[bit] = State::Sx; + + pool<ModWalker::PortBit> portbits; + modwalker.get_drivers(portbits, {bit}); + + if (portbits.size() != 1) + return State::Sx; + + ModWalker::PortBit portbit = *portbits.begin(); + RTLIL::Cell *cell = portbit.cell; + + if (RTLIL::builtin_ff_cell_types().count(cell->type)) + { + FfData ff(&initvals, cell); + + if (ff.has_aload || ff.has_sr || ff.has_arst || (!ff.has_clk && !ff.has_gclk)) { + for (auto bit_q : ff.sig_q) { + initconst_bits[sigmap(bit_q)] = State::Sx; + } + return State::Sx; + } + + for (int i = 0; i < ff.width; i++) { + initconst_bits[sigmap(ff.sig_q[i])] = ff.val_init[i]; + } + + return ff.val_init[portbit.offset]; + } + + if (cell->type.in(ID($mux), ID($and), ID($or), ID($eq), ID($eqx), ID($initstate))) + { + if (cell->type == ID($mux)) + { + SigBit sig_s = sigmap(cell->getPort(ID::S)); + State init_s = initconst(sig_s); + State init_y; + + if (init_s == State::S0) { + init_y = initconst(cell->getPort(ID::A)[portbit.offset]); + } else if (init_s == State::S1) { + init_y = initconst(cell->getPort(ID::B)[portbit.offset]); + } else { + State init_a = initconst(cell->getPort(ID::A)[portbit.offset]); + State init_b = initconst(cell->getPort(ID::B)[portbit.offset]); + init_y = init_a == init_b ? init_a : State::Sx; + } + initconst_bits[bit] = init_y; + return init_y; + } + + if (cell->type.in(ID($and), ID($or))) + { + State init_a = initconst(bit_in_port(cell, ID::A, ID::A_SIGNED, portbit.offset)); + State init_b = initconst(bit_in_port(cell, ID::B, ID::B_SIGNED, portbit.offset)); + State init_y; + if (init_a == init_b) + init_y = init_a; + else if (cell->type == ID($and) && (init_a == State::S0 || init_b == State::S0)) + init_y = State::S0; + else if (cell->type == ID($or) && (init_a == State::S1 || init_b == State::S1)) + init_y = State::S1; + else + init_y = State::Sx; + + initconst_bits[bit] = init_y; + return init_y; + } + + if (cell->type.in(ID($eq), ID($eqx))) // Treats $eqx as $eq + { + if (portbit.offset > 0) { + initconst_bits[bit] = State::S0; + return State::S0; + } + + SigSpec sig_a = cell->getPort(ID::A); + SigSpec sig_b = cell->getPort(ID::B); + + State init_y = State::S1; + + for (int i = 0; init_y != State::S0 && i < GetSize(sig_a); i++) { + State init_ai = initconst(bit_in_port(cell, ID::A, ID::A_SIGNED, i)); + if (init_ai == State::Sx) { + init_y = State::Sx; + continue; + } + State init_bi = initconst(bit_in_port(cell, ID::B, ID::B_SIGNED, i)); + if (init_bi == State::Sx) + init_y = State::Sx; + else if (init_ai != init_bi) + init_y = State::S0; + } + + initconst_bits[bit] = init_y; + return init_y; + } + + if (cell->type == ID($initstate)) + { + initconst_bits[bit] = State::S1; + return State::S1; + } + + log_assert(false); + } + + return State::Sx; + } + + RTLIL::Const initconst(SigSpec sig) + { + std::vector<RTLIL::State> bits; + for (auto bit : sig) + bits.push_back(initconst(bit)); + return bits; + } + + // Is the initial value of this signal used? + // + // An initial value of a signal is considered as used if it a) aliases a + // wire with a public name, an output wire or with a keep attribute b) + // combinationally drives such a wire or c) drive an input to an unknown + // cell. + // + // This recurses into driven cells for a small number of known handled + // celltypes. Results are cached and initconst is used to detect unused + // inputs for the handled celltypes. + bool is_initval_used(SigBit bit) + { + if (!bit.is_wire()) + return false; + + auto it = used_bits.find(bit); + if (it != used_bits.end()) + return it->second; + + used_bits[bit] = true; // Temporarily set to guard against logic loops + + pool<ModWalker::PortBit> portbits; + modwalker.get_consumers(portbits, {bit}); + + for (auto portbit : portbits) { + RTLIL::Cell *cell = portbit.cell; + if (!cell->type.in(ID($mux), ID($and), ID($or), ID($mem_v2)) && !RTLIL::builtin_ff_cell_types().count(cell->type)) { + return true; + } + } + + for (auto portbit : portbits) + { + RTLIL::Cell *cell = portbit.cell; + if (RTLIL::builtin_ff_cell_types().count(cell->type)) + { + FfData ff(&initvals, cell); + if (ff.has_aload || ff.has_sr || ff.has_arst || ff.has_gclk || !ff.has_clk) + return true; + if (ff.has_ce && initconst(ff.sig_ce.as_bit()) == (ff.pol_ce ? State::S0 : State::S1)) + continue; + if (ff.has_srst && initconst(ff.sig_ce.as_bit()) == (ff.pol_srst ? State::S1 : State::S0)) + continue; + + return true; + } + else if (cell->type == ID($mux)) + { + State init_s = initconst(cell->getPort(ID::S).as_bit()); + if (init_s == State::S0 && portbit.port == ID::B) + continue; + if (init_s == State::S1 && portbit.port == ID::A) + continue; + auto sig_y = cell->getPort(ID::Y); + + if (is_initval_used(sig_y[portbit.offset])) + return true; + } + else if (cell->type.in(ID($and), ID($or))) + { + auto sig_a = cell->getPort(ID::A); + auto sig_b = cell->getPort(ID::B); + auto sig_y = cell->getPort(ID::Y); + if (GetSize(sig_y) != GetSize(sig_a) || GetSize(sig_y) != GetSize(sig_b)) + return true; // TODO handle more of this + State absorbing = cell->type == ID($and) ? State::S0 : State::S1; + if (portbit.port == ID::A && initconst(sig_b[portbit.offset]) == absorbing) + continue; + if (portbit.port == ID::B && initconst(sig_a[portbit.offset]) == absorbing) + continue; + + if (is_initval_used(sig_y[portbit.offset])) + return true; + } + else if (cell->type == ID($mem_v2)) + { + // TODO Use mem.h instead to uniformily cover all cases, most + // likely requires processing all memories when initializing + // the worker + if (!portbit.port.in(ID::WR_DATA, ID::WR_ADDR, ID::RD_ADDR)) + return true; + + if (portbit.port == ID::WR_DATA) + { + if (initconst(cell->getPort(ID::WR_EN)[portbit.offset]) == State::S0) + continue; + } + else if (portbit.port == ID::WR_ADDR) + { + int port = portbit.offset / cell->getParam(ID::ABITS).as_int(); + auto sig_en = cell->getPort(ID::WR_EN); + int width = cell->getParam(ID::WIDTH).as_int(); + + for (int i = port * width; i < (port + 1) * width; i++) + if (initconst(sig_en[i]) != State::S0) + return true; + + continue; + } + else if (portbit.port == ID::RD_ADDR) + { + int port = portbit.offset / cell->getParam(ID::ABITS).as_int(); + auto sig_en = cell->getPort(ID::RD_EN); + + if (initconst(sig_en[port]) != State::S0) + return true; + + continue; + } + else + return true; + } + else + log_assert(false); + } + + used_bits[bit] = false; + return false; + } +}; + +struct FormalFfPass : public Pass { + FormalFfPass() : Pass("formalff", "prepare FFs for formal") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" formalff [options] [selection]\n"); + log("\n"); + log("This pass transforms clocked flip-flops to prepare a design for formal\n"); + log("verification. If a design contains latches and/or multiple different clocks run\n"); + log("the async2sync or clk2fflogic passes before using this pass.\n"); + log("\n"); + log(" -clk2ff\n"); + log(" Replace all clocked flip-flops with $ff cells that use the implicit\n"); + log(" global clock. This assumes, without checking, that the design uses a\n"); + log(" single global clock. If that is not the case, the clk2fflogic pass\n"); + log(" should be used instead.\n"); + log("\n"); + log(" -ff2anyinit\n"); + log(" Replace uninitialized bits of $ff cells with $anyinit cells. An\n"); + log(" $anyinit cell behaves exactly like an $ff cell with an undefined\n"); + log(" initialization value. The difference is that $anyinit inhibits\n"); + log(" don't-care optimizations and is used to track solver-provided values\n"); + log(" in witness traces.\n"); + log("\n"); + log(" If combined with -clk2ff this also affects newly created $ff cells.\n"); + log("\n"); + log(" -anyinit2ff\n"); + log(" Replaces $anyinit cells with uninitialized $ff cells. This performs the\n"); + log(" reverse of -ff2anyinit and can be used, before running a backend pass\n"); + log(" (or similar) that is not yet aware of $anyinit cells.\n"); + log("\n"); + log(" Note that after running -anyinit2ff, in general, performing don't-care\n"); + log(" optimizations is not sound in a formal verification setting.\n"); + log("\n"); + log(" -fine\n"); + log(" Emit fine-grained $_FF_ cells instead of coarse-grained $ff cells for\n"); + log(" -anyinit2ff. Cannot be combined with -clk2ff or -ff2anyinit.\n"); + log("\n"); + log(" -setundef\n"); + log(" Find FFs with undefined initialization values for which changing the\n"); + log(" initialization does not change the observable behavior and initialize\n"); + log(" them. For -ff2anyinit, this reduces the number of generated $anyinit\n"); + log(" cells that drive wires with private names.\n"); + log("\n"); + + // TODO: An option to check whether all FFs use the same clock before changing it to the global clock + } + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + bool flag_clk2ff = false; + bool flag_ff2anyinit = false; + bool flag_anyinit2ff = false; + bool flag_fine = false; + bool flag_setundef = false; + + log_header(design, "Executing FORMALFF pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-clk2ff") { + flag_clk2ff = true; + continue; + } + if (args[argidx] == "-ff2anyinit") { + flag_ff2anyinit = true; + continue; + } + if (args[argidx] == "-anyinit2ff") { + flag_anyinit2ff = true; + continue; + } + if (args[argidx] == "-fine") { + flag_fine = true; + continue; + } + if (args[argidx] == "-setundef") { + flag_setundef = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!(flag_clk2ff || flag_ff2anyinit || flag_anyinit2ff)) + log_cmd_error("One of the options -clk2ff, -ff2anyinit, or -anyinit2ff must be specified.\n"); + + if (flag_ff2anyinit && flag_anyinit2ff) + log_cmd_error("The options -ff2anyinit and -anyinit2ff are exclusive.\n"); + + if (flag_fine && !flag_anyinit2ff) + log_cmd_error("The option -fine requries the -anyinit2ff option.\n"); + + if (flag_fine && flag_clk2ff) + log_cmd_error("The options -fine and -clk2ff are exclusive.\n"); + + for (auto module : design->selected_modules()) + { + if (flag_setundef) + { + InitValWorker worker(module); + + for (auto cell : module->selected_cells()) + { + if (RTLIL::builtin_ff_cell_types().count(cell->type)) + { + FfData ff(&worker.initvals, cell); + if (ff.has_aload || ff.has_sr || ff.has_arst || ff.val_init.is_fully_def()) + continue; + + if (ff.has_ce && // CE can make the initval stick around + worker.initconst(ff.sig_ce.as_bit()) != (ff.pol_ce ? State::S1 : State::S0) && // unless it's known active + (!ff.has_srst || ff.ce_over_srst || + worker.initconst(ff.sig_srst.as_bit()) != (ff.pol_srst ? State::S1 : State::S0))) // or a srst with priority is known active + continue; + + auto before = ff.val_init; + for (int i = 0; i < ff.width; i++) + if (ff.val_init[i] == State::Sx && !worker.is_initval_used(ff.sig_q[i])) + ff.val_init[i] = State::S0; + + if (ff.val_init != before) { + log("Setting unused undefined initial value of %s.%s (%s) from %s to %s\n", + log_id(module), log_id(cell), log_id(cell->type), + log_const(before), log_const(ff.val_init)); + worker.initvals.set_init(ff.sig_q, ff.val_init); + } + } + } + } + SigMap sigmap(module); + FfInitVals initvals(&sigmap, module); + + + for (auto cell : module->selected_cells()) + { + if (flag_anyinit2ff && cell->type == ID($anyinit)) + { + FfData ff(&initvals, cell); + ff.remove(); + ff.is_anyinit = false; + ff.is_fine = flag_fine; + if (flag_fine) + for (int i = 0; i < ff.width; i++) + ff.slice({i}).emit(); + else + ff.emit(); + + continue; + } + + if (!RTLIL::builtin_ff_cell_types().count(cell->type)) + continue; + + FfData ff(&initvals, cell); + bool emit = false; + + if (flag_clk2ff && ff.has_clk) { + if (ff.sig_clk.is_fully_const()) + log_error("Const CLK on %s (%s) from module %s, run async2sync first.\n", + log_id(cell), log_id(cell->type), log_id(module)); + + auto clk_wire = ff.sig_clk.is_wire() ? ff.sig_clk.as_wire() : nullptr; + + if (clk_wire == nullptr) { + clk_wire = module->addWire(NEW_ID); + module->connect(RTLIL::SigBit(clk_wire), ff.sig_clk); + } + + auto clk_polarity = ff.pol_clk ? State::S1 : State::S0; + + std::string attribute = clk_wire->get_string_attribute(ID::replaced_by_gclk); + + auto &attr = clk_wire->attributes[ID::replaced_by_gclk]; + + if (!attr.empty() && attr != clk_polarity) + log_error("CLK %s on %s (%s) from module %s also used with opposite polarity, run clk2fflogic instead.\n", + log_id(clk_wire), log_id(cell), log_id(cell->type), log_id(module)); + + attr = clk_polarity; + clk_wire->set_bool_attribute(ID::keep); + + // TODO propagate the replaced_by_gclk attribute upwards throughout the hierarchy + + ff.unmap_ce_srst(); + ff.has_clk = false; + ff.has_gclk = true; + emit = true; + } + + if (!ff.has_gclk) { + continue; + } + + if (flag_ff2anyinit && !ff.val_init.is_fully_def()) + { + ff.remove(); + emit = false; + + int cursor = 0; + while (cursor < ff.val_init.size()) + { + bool is_anyinit = ff.val_init[cursor] == State::Sx; + std::vector<int> bits; + bits.push_back(cursor++); + while (cursor < ff.val_init.size() && (ff.val_init[cursor] == State::Sx) == is_anyinit) + bits.push_back(cursor++); + + if ((int)bits.size() == ff.val_init.size()) { + // This check is only to make the private names more helpful for debugging + ff.is_anyinit = true; + emit = true; + break; + } + + auto slice = ff.slice(bits); + slice.is_anyinit = is_anyinit; + slice.emit(); + } + } + + if (emit) + ff.emit(); + } + } + } +} FormalFfPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/sat/qbfsat.cc b/passes/sat/qbfsat.cc index 864d6f05d..1302b3383 100644 --- a/passes/sat/qbfsat.cc +++ b/passes/sat/qbfsat.cc @@ -216,7 +216,7 @@ QbfSolutionType call_qbf_solver(RTLIL::Module *mod, const QbfSolveOptions &opt, QbfSolutionType ret; const std::string yosys_smtbmc_exe = proc_self_dirname() + "yosys-smtbmc"; const std::string smtbmc_warning = "z3: WARNING:"; - const std::string smtbmc_cmd = stringf("%s -s %s %s -t 1 -g --binary %s %s/problem%d.smt2 2>&1", + const std::string smtbmc_cmd = stringf("\"%s\" -s %s %s -t 1 -g --binary %s %s/problem%d.smt2 2>&1", yosys_smtbmc_exe.c_str(), opt.get_solver_name().c_str(), (opt.timeout != 0? stringf("--timeout %d", opt.timeout) : "").c_str(), (opt.dump_final_smt2? "--dump-smt2 " + opt.dump_final_smt2_file : "").c_str(), diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index d085fab2d..18a25a097 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -81,6 +81,7 @@ struct SimShared bool hide_internal = true; bool writeback = false; bool zinit = false; + bool hdlname = false; int rstlen = 1; FstData *fst = nullptr; double start_time = 0; @@ -157,6 +158,7 @@ struct SimInstance dict<Wire*, pair<int, Const>> signal_database; dict<Wire*, fstHandle> fst_handles; + dict<Wire*, fstHandle> fst_inputs; dict<IdString, dict<int,fstHandle>> fst_memories; SimInstance(SimShared *shared, std::string scope, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) : @@ -230,7 +232,7 @@ struct SimInstance } } - if (RTLIL::builtin_ff_cell_types().count(cell->type)) { + if (RTLIL::builtin_ff_cell_types().count(cell->type) || cell->type == ID($anyinit)) { FfData ff_data(nullptr, cell); ff_state_t ff; ff.past_d = Const(State::Sx, ff_data.width); @@ -737,9 +739,17 @@ struct SimInstance child.second->register_signals(id); } - void write_output_header(std::function<void(IdString)> enter_scope, std::function<void()> exit_scope, std::function<void(Wire*, int, bool)> register_signal) + void write_output_header(std::function<void(IdString)> enter_scope, std::function<void()> exit_scope, std::function<void(const char*, Wire*, int, bool)> register_signal) { - enter_scope(name()); + int exit_scopes = 1; + if (shared->hdlname && instance != nullptr && instance->name.isPublic() && instance->has_attribute(ID::hdlname)) { + auto hdlname = instance->get_hdlname_attribute(); + log_assert(!hdlname.empty()); + for (auto name : hdlname) + enter_scope("\\" + name); + exit_scopes = hdlname.size(); + } else + enter_scope(name()); dict<Wire*,bool> registers; for (auto cell : module->cells()) @@ -755,13 +765,25 @@ struct SimInstance for (auto signal : signal_database) { - register_signal(signal.first, signal.second.first, registers.count(signal.first)!=0); + if (shared->hdlname && signal.first->name.isPublic() && signal.first->has_attribute(ID::hdlname)) { + auto hdlname = signal.first->get_hdlname_attribute(); + log_assert(!hdlname.empty()); + auto signal_name = std::move(hdlname.back()); + hdlname.pop_back(); + for (auto name : hdlname) + enter_scope("\\" + name); + register_signal(signal_name.c_str(), signal.first, signal.second.first, registers.count(signal.first)!=0); + for (auto name : hdlname) + exit_scope(); + } else + register_signal(log_id(signal.first->name), signal.first, signal.second.first, registers.count(signal.first)!=0); } for (auto child : children) child.second->write_output_header(enter_scope, exit_scope, register_signal); - exit_scope(); + for (int i = 0; i < exit_scopes; i++) + exit_scope(); } void register_output_step_values(std::map<int,Const> *data) @@ -820,7 +842,7 @@ struct SimInstance return did_something; } - void addAdditionalInputs(std::map<Wire*,fstHandle> &inputs) + void addAdditionalInputs() { for (auto cell : module->cells()) { @@ -831,7 +853,7 @@ struct SimInstance for(auto &item : fst_handles) { if (item.second==0) continue; // Ignore signals not found if (sig_y == sigmap(item.first)) { - inputs[sig_y.as_wire()] = item.second; + fst_inputs[sig_y.as_wire()] = item.second; found = true; break; } @@ -842,7 +864,21 @@ struct SimInstance } } for (auto child : children) - child.second->addAdditionalInputs(inputs); + child.second->addAdditionalInputs(); + } + + bool setInputs() + { + bool did_something = false; + for(auto &item : fst_inputs) { + std::string v = shared->fst->valueOf(item.second); + did_something |= set_state(item.first, Const::from_string(v)); + } + + for (auto child : children) + did_something |= child.second->setInputs(); + + return did_something; } void setState(dict<int, std::pair<SigBit,bool>> bits, std::string values) @@ -1095,18 +1131,17 @@ struct SimWorker : SimShared } 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; + top->fst_inputs[wire] = id; } } - top->addAdditionalInputs(inputs); + top->addAdditionalInputs(); uint64_t startCount = 0; uint64_t stopCount = 0; @@ -1152,11 +1187,7 @@ struct SimWorker : SimShared fst->reconstructAllAtTimes(fst_clock, startCount, stopCount, [&](uint64_t time) { 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); - did_something |= top->set_state(item.first, Const::from_string(v)); - } + bool did_something = top->setInputs(); if (initial) { did_something |= top->setInitState(); @@ -1702,7 +1733,11 @@ struct VCDWriter : public OutputWriter 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, bool is_reg) { if (use_signal.at(id)) vcdfile << stringf("$var %s %d n%d %s%s $end\n", is_reg ? "reg" : "wire", GetSize(wire), id, wire->name[0] == '$' ? "\\" : "", log_id(wire)); } + [this,use_signal](const char *name, Wire *wire, int id, bool is_reg) { + if (use_signal.at(id)) { + vcdfile << stringf("$var %s %d n%d %s%s $end\n", is_reg ? "reg" : "wire", GetSize(wire), id, name[0] == '$' ? "\\" : "", name); + } + } ); vcdfile << stringf("$enddefinitions $end\n"); @@ -1760,11 +1795,10 @@ struct FSTWriter : public OutputWriter 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, bool is_reg) { + [this,use_signal](const char *name, Wire *wire, int id, bool is_reg) { if (!use_signal.at(id)) return; fstHandle fst_id = fstWriterCreateVar(fstfile, is_reg ? FST_VT_VCD_REG : FST_VT_VCD_WIRE, FST_VD_IMPLICIT, GetSize(wire), - stringf("%s%s", wire->name[0] == '$' ? "\\" : "", log_id(wire)).c_str(), 0); - + name, 0); mapping.emplace(id, fst_id); } ); @@ -1846,7 +1880,7 @@ struct AIWWriter : public OutputWriter worker->top->write_output_header( [](IdString) {}, []() {}, - [this](Wire *wire, int id, bool) { mapping[wire] = id; } + [this](const char */*name*/, Wire *wire, int id, bool) { mapping[wire] = id; } ); std::map<int, Yosys::RTLIL::Const> current; @@ -1935,6 +1969,10 @@ struct SimPass : public Pass { log(" write the simulation results to an AIGER witness file\n"); log(" (requires a *.aim file via -map)\n"); log("\n"); + log(" -hdlname\n"); + log(" use the hdlname attribute when writing simulation results\n"); + log(" (preserves hierarchy in a flattened design)\n"); + log("\n"); log(" -x\n"); log(" ignore constant x outputs in simulation file.\n"); log("\n"); @@ -2047,6 +2085,10 @@ struct SimPass : public Pass { worker.outputfiles.emplace_back(std::unique_ptr<AIWWriter>(new AIWWriter(&worker, aiw_filename.c_str()))); continue; } + if (args[argidx] == "-hdlname") { + worker.hdlname = true; + continue; + } if (args[argidx] == "-n" && argidx+1 < args.size()) { numcycles = atoi(args[++argidx].c_str()); worker.cycles_set = true; diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 61ee99ee7..656c36b84 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -65,7 +65,9 @@ #include "frontends/blif/blifparse.h" #ifdef YOSYS_LINK_ABC -extern "C" int Abc_RealMain(int argc, char *argv[]); +namespace abc { + int Abc_RealMain(int argc, char *argv[]); +} #endif USING_YOSYS_NAMESPACE @@ -787,15 +789,15 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin log_header(design, "Extracting gate netlist of module `%s' to `%s/input.blif'..\n", module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str()); - std::string abc_script = stringf("read_blif %s/input.blif; ", tempdir_name.c_str()); + std::string abc_script = stringf("read_blif \"%s/input.blif\"; ", tempdir_name.c_str()); if (!liberty_files.empty() || !genlib_files.empty()) { for (std::string liberty_file : liberty_files) - abc_script += stringf("read_lib -w %s; ", liberty_file.c_str()); + abc_script += stringf("read_lib -w \"%s\"; ", liberty_file.c_str()); for (std::string liberty_file : genlib_files) - abc_script += stringf("read_library %s; ", liberty_file.c_str()); + abc_script += stringf("read_library \"%s\"; ", liberty_file.c_str()); if (!constr_file.empty()) - abc_script += stringf("read_constr -v %s; ", constr_file.c_str()); + abc_script += stringf("read_constr -v \"%s\"; ", constr_file.c_str()); } else if (!lut_costs.empty()) abc_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); @@ -1083,7 +1085,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin fclose(f); } - buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); + buffer = stringf("\"%s\" -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); #ifndef YOSYS_LINK_ABC @@ -1098,7 +1100,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin abc_argv[2] = strdup("-f"); abc_argv[3] = strdup(tmp_script_name.c_str()); abc_argv[4] = 0; - int ret = Abc_RealMain(4, abc_argv); + int ret = abc::Abc_RealMain(4, abc_argv); free(abc_argv[0]); free(abc_argv[1]); free(abc_argv[2]); diff --git a/passes/techmap/abc9_exe.cc b/passes/techmap/abc9_exe.cc index a66e95e21..2f46c89f4 100644 --- a/passes/techmap/abc9_exe.cc +++ b/passes/techmap/abc9_exe.cc @@ -31,7 +31,9 @@ #endif #ifdef YOSYS_LINK_ABC -extern "C" int Abc_RealMain(int argc, char *argv[]); +namespace abc { + int Abc_RealMain(int argc, char *argv[]); +} #endif std::string fold_abc9_cmd(std::string str) @@ -173,12 +175,12 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe if (!lut_costs.empty()) abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); else if (!lut_file.empty()) - abc9_script += stringf("read_lut %s; ", lut_file.c_str()); + abc9_script += stringf("read_lut \"%s\"; ", lut_file.c_str()); else log_abort(); log_assert(!box_file.empty()); - abc9_script += stringf("read_box %s; ", box_file.c_str()); + abc9_script += stringf("read_box \"%s\"; ", box_file.c_str()); abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str()); if (!script_file.empty()) { @@ -262,7 +264,7 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe fclose(f); } - buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); + buffer = stringf("\"%s\" -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); #ifndef YOSYS_LINK_ABC @@ -277,7 +279,7 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe abc9_argv[2] = strdup("-f"); abc9_argv[3] = strdup(tmp_script_name.c_str()); abc9_argv[4] = 0; - int ret = Abc_RealMain(4, abc9_argv); + int ret = abc::Abc_RealMain(4, abc9_argv); free(abc9_argv[0]); free(abc9_argv[1]); free(abc9_argv[2]); diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index b14488ff4..ab9bd7e1d 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -1697,6 +1697,23 @@ assign Y = 'bx; endmodule // -------------------------------------------------------- +`ifdef SIMLIB_FF +module \$anyinit (D, Q); + +parameter WIDTH = 0; + +input [WIDTH-1:0] D; +output reg [WIDTH-1:0] Q; + +initial Q <= 'bx; + +always @($global_clk) begin + Q <= D; +end + +endmodule +`endif +// -------------------------------------------------------- module \$allconst (Y); diff --git a/techlibs/gatemate/.gitignore b/techlibs/gatemate/.gitignore new file mode 100644 index 000000000..f260d6e9d --- /dev/null +++ b/techlibs/gatemate/.gitignore @@ -0,0 +1,4 @@ +lut_tree_cells.genlib +lut_tree_map.v +lut_tree_lib.mk + diff --git a/techlibs/gatemate/Makefile.inc b/techlibs/gatemate/Makefile.inc index d1341d7bb..aeb318cc9 100644 --- a/techlibs/gatemate/Makefile.inc +++ b/techlibs/gatemate/Makefile.inc @@ -1,5 +1,6 @@ OBJS += techlibs/gatemate/synth_gatemate.o
+OBJS += techlibs/gatemate/gatemate_foldinv.o
$(eval $(call add_share_file,share/gatemate,techlibs/gatemate/reg_map.v))
$(eval $(call add_share_file,share/gatemate,techlibs/gatemate/mux_map.v))
@@ -12,3 +13,18 @@ $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/brams_map.v)) $(eval $(call add_share_file,share/gatemate,techlibs/gatemate/brams.txt))
$(eval $(call add_share_file,share/gatemate,techlibs/gatemate/brams_init_20.vh))
$(eval $(call add_share_file,share/gatemate,techlibs/gatemate/brams_init_40.vh))
+$(eval $(call add_share_file,share/gatemate,techlibs/gatemate/inv_map.v))
+
+EXTRA_OBJS += techlibs/gatemate/lut_tree_lib.mk
+.SECONDARY: techlibs/gatemate/lut_tree_lib.mk
+
+techlibs/gatemate/lut_tree_lib.mk: techlibs/gatemate/make_lut_tree_lib.py
+ $(Q) mkdir -p techlibs/gatemate
+ $(P) $(PYTHON_EXECUTABLE) $<
+ $(Q) touch $@
+
+techlibs/gatemate/lut_tree_cells.genlib: techlibs/gatemate/lut_tree_lib.mk
+techlibs/gatemate/lut_tree_map.v: techlibs/gatemate/lut_tree_lib.mk
+
+$(eval $(call add_gen_share_file,share/gatemate,techlibs/gatemate/lut_tree_cells.genlib))
+$(eval $(call add_gen_share_file,share/gatemate,techlibs/gatemate/lut_tree_map.v))
diff --git a/techlibs/gatemate/cells_sim.v b/techlibs/gatemate/cells_sim.v index 1de3d1c7a..7e88fd7cf 100644 --- a/techlibs/gatemate/cells_sim.v +++ b/techlibs/gatemate/cells_sim.v @@ -1409,3 +1409,47 @@ module CC_BRAM_40K ( end
endgenerate
endmodule
+
+// Models of the LUT2 tree primitives
+module CC_L2T4(
+ output O,
+ input I0, I1, I2, I3
+);
+ parameter [3:0] INIT_L00 = 4'b0000;
+ parameter [3:0] INIT_L01 = 4'b0000;
+ parameter [3:0] INIT_L10 = 4'b0000;
+
+ wire [1:0] l00_s1 = I1 ? INIT_L00[3:2] : INIT_L00[1:0];
+ wire l00 = I0 ? l00_s1[1] : l00_s1[0];
+
+ wire [1:0] l01_s1 = I3 ? INIT_L01[3:2] : INIT_L01[1:0];
+ wire l01 = I2 ? l01_s1[1] : l01_s1[0];
+
+ wire [1:0] l10_s1 = l01 ? INIT_L10[3:2] : INIT_L10[1:0];
+ assign O = l00 ? l10_s1[1] : l10_s1[0];
+
+endmodule
+
+
+module CC_L2T5(
+ output O,
+ input I0, I1, I2, I3, I4
+);
+ parameter [3:0] INIT_L02 = 4'b0000;
+ parameter [3:0] INIT_L03 = 4'b0000;
+ parameter [3:0] INIT_L11 = 4'b0000;
+ parameter [3:0] INIT_L20 = 4'b0000;
+
+ wire [1:0] l02_s1 = I1 ? INIT_L02[3:2] : INIT_L02[1:0];
+ wire l02 = I0 ? l02_s1[1] : l02_s1[0];
+
+ wire [1:0] l03_s1 = I3 ? INIT_L03[3:2] : INIT_L03[1:0];
+ wire l03 = I2 ? l03_s1[1] : l03_s1[0];
+
+ wire [1:0] l11_s1 = l03 ? INIT_L11[3:2] : INIT_L11[1:0];
+ wire l11 = l02 ? l11_s1[1] : l11_s1[0];
+
+ wire [1:0] l20_s1 = l11 ? INIT_L20[3:2] : INIT_L20[1:0];
+ assign O = I4 ? l20_s1[1] : l20_s1[0];
+
+endmodule
diff --git a/techlibs/gatemate/gatemate_foldinv.cc b/techlibs/gatemate/gatemate_foldinv.cc new file mode 100644 index 000000000..752f8aac0 --- /dev/null +++ b/techlibs/gatemate/gatemate_foldinv.cc @@ -0,0 +1,219 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 gatecat <gatecat@ds0.me> + * Copyright (C) 2021 Cologne Chip AG <support@colognechip.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/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct LUTPin { + int input_bit; + IdString init_param; +}; + +struct LUTType { + dict<IdString, LUTPin> inputs; + IdString output_param; +}; + +struct FoldInvWorker { + FoldInvWorker(Module *module) : module(module), sigmap(module) {}; + Module *module; + SigMap sigmap; + + // Mapping from inverter output to inverter input + dict<SigBit, SigBit> inverted_bits; + // Mapping from inverter input to inverter + dict<SigBit, Cell*> inverter_input; + + const dict<IdString, LUTType> lut_types = { + {ID(CC_LUT2), {{ + {ID(I0), {0, ID(INIT)}}, + {ID(I1), {1, ID(INIT)}}, + }, ID(INIT)}}, + {ID(CC_L2T4), {{ + {ID(I0), {0, ID(INIT_L00)}}, + {ID(I1), {1, ID(INIT_L00)}}, + {ID(I2), {0, ID(INIT_L01)}}, + {ID(I3), {1, ID(INIT_L01)}}, + }, ID(INIT_L10)}}, + {ID(CC_L2T5), {{ + {ID(I0), {0, ID(INIT_L02)}}, + {ID(I1), {1, ID(INIT_L02)}}, + {ID(I2), {0, ID(INIT_L03)}}, + {ID(I3), {1, ID(INIT_L03)}}, + {ID(I4), {0, ID(INIT_L20)}}, + }, ID(INIT_L20)}}, + }; + + + void find_inverted_bits() + { + for (auto cell : module->selected_cells()) { + if (cell->type != ID($__CC_NOT)) + continue; + SigBit a = sigmap(cell->getPort(ID::A)[0]); + SigBit y = sigmap(cell->getPort(ID::Y)[0]); + inverted_bits[y] = a; + inverter_input[a] = cell; + } + } + + Const invert_lut_input(Const lut, int bit) + { + Const result(State::S0, GetSize(lut)); + for (int i = 0; i < GetSize(lut); i++) { + int j = i ^ (1 << bit); + result[j] = lut[i]; + } + return result; + } + + Const invert_lut_output(Const lut) + { + Const result(State::S0, GetSize(lut)); + for (int i = 0; i < GetSize(lut); i++) + result[i] = (lut[i] == State::S1) ? State::S0 : State::S1; + return result; + } + + void fold_input_inverters() + { + for (auto cell : module->selected_cells()) { + auto found_type = lut_types.find(cell->type); + if (found_type == lut_types.end()) + continue; + const auto &type = found_type->second; + for (const auto &ipin : type.inputs) { + if (!cell->hasPort(ipin.first)) + continue; + auto sig = cell->getPort(ipin.first); + if (GetSize(sig) == 0) + continue; + SigBit bit = sigmap(sig[0]); + auto inv = inverted_bits.find(bit); + if (inv == inverted_bits.end()) + continue; // not the output of an inverter + // Rewire to inverter input + cell->unsetPort(ipin.first); + cell->setPort(ipin.first, inv->second); + // Rewrite init + cell->setParam(ipin.second.init_param, + invert_lut_input(cell->getParam(ipin.second.init_param), ipin.second.input_bit)); + } + } + } + + void fold_output_inverters() + { + pool<SigBit> used_bits; + // Find bits that are actually used + for (auto cell : module->selected_cells()) { + for (auto conn : cell->connections()) { + if (cell->output(conn.first)) + continue; + for (auto bit : sigmap(conn.second)) + used_bits.insert(bit); + } + } + // Find LUTs driving inverters + // (create a vector to avoid iterate-and-modify issues) + std::vector<std::pair<Cell *, Cell*>> lut_inv; + for (auto cell : module->selected_cells()) { + auto found_type = lut_types.find(cell->type); + if (found_type == lut_types.end()) + continue; + if (!cell->hasPort(ID::O)) + continue; + auto o_sig = cell->getPort(ID::O); + if (GetSize(o_sig) == 0) + continue; + SigBit o = sigmap(o_sig[0]); + auto found_inv = inverter_input.find(o); + if (found_inv == inverter_input.end()) + continue; // doesn't drive an inverter + lut_inv.emplace_back(cell, found_inv->second); + } + for (auto pair : lut_inv) { + Cell *orig_lut = pair.first; + Cell *inv = pair.second; + // Find the inverter output + SigBit inv_y = sigmap(inv->getPort(ID::Y)[0]); + // Inverter output might not actually be used; if all users were folded into inputs already + if (!used_bits.count(inv_y)) + continue; + // Create a duplicate of the LUT with an inverted output + // (if the uninverted version becomes unused it will be swept away) + Cell *dup_lut = module->addCell(NEW_ID, orig_lut->type); + inv->unsetPort(ID::Y); + dup_lut->setPort(ID::O, inv_y); + for (auto conn : orig_lut->connections()) { + if (conn.first == ID::O) + continue; + dup_lut->setPort(conn.first, conn.second); + } + for (auto param : orig_lut->parameters) { + if (param.first == lut_types.at(orig_lut->type).output_param) + dup_lut->parameters[param.first] = invert_lut_output(param.second); + else + dup_lut->parameters[param.first] = param.second; + } + } + } + + void operator()() + { + find_inverted_bits(); + fold_input_inverters(); + fold_output_inverters(); + } +}; + +struct GatemateFoldInvPass : public Pass { + GatemateFoldInvPass() : Pass("gatemate_foldinv", "fold inverters into Gatemate LUT trees") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" gatemate_foldinv [selection]\n"); + log("\n"); + log("\n"); + log("This pass searches for $__CC_NOT cells and folds them into CC_LUT2, CC_L2T4\n"); + log("and CC_L2T5 cells as created by LUT tree mapping.\n"); + log("\n"); + } + + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + log_header(design, "Executing GATEMATE_FOLDINV pass (folding LUT tree inverters).\n"); + + size_t argidx = 1; + extra_args(args, argidx, design); + + for (Module *module : design->selected_modules()) { + FoldInvWorker worker(module); + worker(); + } + } +} GatemateFoldInvPass; + +PRIVATE_NAMESPACE_END + diff --git a/techlibs/gatemate/inv_map.v b/techlibs/gatemate/inv_map.v new file mode 100644 index 000000000..8a5051747 --- /dev/null +++ b/techlibs/gatemate/inv_map.v @@ -0,0 +1,4 @@ +// Any inverters not folded into LUTs are mapped to a LUT of their own +module \$__CC_NOT (input A, output Y); + CC_LUT1 #(.INIT(2'b01)) _TECHMAP_REPLACE_ (.I0(A), .O(Y)); +endmodule diff --git a/techlibs/gatemate/make_lut_tree_lib.py b/techlibs/gatemate/make_lut_tree_lib.py new file mode 100644 index 000000000..25ca88882 --- /dev/null +++ b/techlibs/gatemate/make_lut_tree_lib.py @@ -0,0 +1,323 @@ +#!/usr/bin/env python3 + +class FNode: + def __init__(self, fun, *args): + self.fun = fun + self.args = args + + if len(self.args) == 0: + assert fun not in ("BUF", "NOT", "AND", "OR", "XOR", "MUX") + + if len(self.args) == 1: + assert fun in ("BUF", "NOT") + + if len(self.args) == 2: + assert fun in ("AND", "OR", "XOR") + + if len(self.args) == 3: + assert fun in ("MUX") + + def __str__(self): + if len(self.args) == 0: + return self.fun + if self.fun == "NOT" and len(self.args[0].args) == 0: + return "!" + self.args[0].fun + return self.fun + "(" + ",".join([str(a) for a in self.args]) + ")" + + def as_genlib_term(self): + if len(self.args) == 0: + return self.fun + if self.fun == "NOT": + assert len(self.args[0].args) == 0 + return "!" + self.args[0].fun + if self.fun == "AND": + return "(" + self.args[0].as_genlib_term() + "*" + self.args[1].as_genlib_term() + ")" + if self.fun == "OR": + return "(" + self.args[0].as_genlib_term() + "+" + self.args[1].as_genlib_term() + ")" + assert False + + def mapMux(self): + if self.fun == "MUX": + A, B, C = self.args + return OR(AND(A, NOT(C)), AND(B, C)).mapMux() + return FNode(self.fun, *[a.mapMux() for a in self.args]) + + def mapXor(self): + if self.fun == "XOR": + A, B = self.args + return OR(AND(A, NOT(B)), AND(NOT(A), B)).mapXor() + return FNode(self.fun, *[a.mapXor() for a in self.args]) + + def mapNot(self): + if self.fun == "BUF": + return self.arg1.mapNot() + if self.fun == "NOT": + if self.args[0].fun == "AND": + return OR(NOT(self.args[0].args[0]),NOT(self.args[0].args[1])).mapNot() + if self.args[0].fun == "OR": + return AND(NOT(self.args[0].args[0]),NOT(self.args[0].args[1])).mapNot() + if self.args[0].fun == "NOT": + return self.args[0].args[0].mapNot() + return FNode(self.fun, *[a.mapNot() for a in self.args]) + + def map(self): + n = self + n = n.mapMux() + n = n.mapXor() + n = n.mapNot() + return n + + def isInv(self): + if len(self.args) == 0: + return False + if self.fun == "XOR": + return False + if self.fun == "NOT": + return self.args[0].isNonInv() + for a in self.args: + if not a.isInv(): + return False + return True + + def isNonInv(self): + if len(self.args) == 0: + return True + if self.fun == "XOR": + return False + if self.fun == "NOT": + return self.args[0].isInv() + for a in self.args: + if not a.isNonInv(): + return False + return True + +A = FNode("A") +B = FNode("B") +C = FNode("C") +D = FNode("D") +E = FNode("E") + +def BUF(arg): return FNode("BUF", arg) +def NOT(arg): return FNode("NOT", arg) +def AND(arg1, arg2): return FNode("AND", arg1, arg2) +def OR(arg1, arg2): return FNode( "OR", arg1, arg2) +def XOR(arg1, arg2): return FNode("XOR", arg1, arg2) +def MUX(arg1, arg2, arg3): return FNode("MUX", arg1, arg2, arg3) + +# Genlib Format: +# +# GATE <cell-name> <cell-area> <cell-logic-function> +# +# PIN <pin-name> <phase> <input-load> <max-load> +# <rise-block-delay> <rise-fanout-delay> +# <fall-block-delay> <fall-fanout-delay> +# +# phase: +# INV, NONINV, or UNKNOWN +# +# cell-logic-function: +# <output> = <term with *(AND), +(OR), !(NOT)> + + +cells = [ + ["$__CC_BUF", 5, A], + ["$__CC_NOT", 0, NOT(A)], + ["$__CC_MUX", 5, MUX(A, B, C)], +] + +base_cells = [ + ["$__CC2_A", AND(A, B)], + ["$__CC2_O", OR(A, B)], + ["$__CC2_X", XOR(A, B)], + + ["$__CC3_AA", AND(AND(A, B), C)], + ["$__CC3_OO", OR( OR(A, B), C)], + ["$__CC3_XX", XOR(XOR(A, B), C)], + ["$__CC3_AO", OR(AND(A, B), C)], + ["$__CC3_OA", AND( OR(A, B), C)], + ["$__CC3_AX", XOR(AND(A, B), C)], + ["$__CC3_XA", AND(XOR(A, B), C)], + +# ["$__CC3_AAA", AND(AND(A,B),AND(A,C))], +# ["$__CC3_AXA", XOR(AND(A,B),AND(A,C))], +# ["$__CC3_XAX", AND(XOR(A,B),XOR(A,C))], +# ["$__CC3_AAX", AND(AND(A,B),XOR(A,C))], +# ["$__CC3_AXX", XOR(AND(A,B),XOR(A,C))], +# ["$__CC3_XXX", XOR(XOR(A,B),XOR(A,C))], +# ["$__CC3_AAO", AND(AND(A,B), OR(A,C))], +# ["$__CC3_AOA", OR(AND(A,B),AND(A,C))], +# ["$__CC3_AOX", OR(AND(A,B),XOR(A,C))], + +# ["$__CC3_AAA_N", AND(AND(A,B),AND(NOT(A),C))], +# ["$__CC3_AXA_N", XOR(AND(A,B),AND(NOT(A),C))], +# ["$__CC3_XAX_N", AND(XOR(A,B),XOR(NOT(A),C))], +# ["$__CC3_AAX_N", AND(AND(A,B),XOR(NOT(A),C))], +# ["$__CC3_AXX_N", XOR(AND(A,B),XOR(NOT(A),C))], +# ["$__CC3_XXX_N", XOR(XOR(A,B),XOR(NOT(A),C))], +# ["$__CC3_AAO_N", AND(AND(A,B), OR(NOT(A),C))], +# ["$__CC3_AOA_N", OR(AND(A,B),AND(NOT(A),C))], +# ["$__CC3_AOX_N", OR(AND(A,B),XOR(NOT(A),C))], + + ["$__CC4_AAA", AND(AND(A,B),AND(C,D))], + ["$__CC4_AXA", XOR(AND(A,B),AND(C,D))], + ["$__CC4_XAX", AND(XOR(A,B),XOR(C,D))], + ["$__CC4_AAX", AND(AND(A,B),XOR(C,D))], + ["$__CC4_AXX", XOR(AND(A,B),XOR(C,D))], + ["$__CC4_XXX", XOR(XOR(A,B),XOR(C,D))], + ["$__CC4_AAO", AND(AND(A,B), OR(C,D))], + ["$__CC4_AOA", OR(AND(A,B),AND(C,D))], + ["$__CC4_AOX", OR(AND(A,B),XOR(C,D))], +] + +for name, expr in base_cells: + cells.append([name, 10, expr]) + + name = (name + .replace("$__CC4_", "$__CC5_") + .replace("$__CC3_", "$__CC4_") + .replace("$__CC2_", "$__CC3_")) + + # Cells such as $__CC4_AA_A are redundant, as $__CC4_AAA is equivalent + if name not in ("$__CC4_AA", "$__CC3_A"): + cells.append([name + "_A", 12, AND(E, expr)]) + if name not in ("$__CC4_OO", "$__CC3_O"): + cells.append([name + "_O", 12, OR(E, expr)]) + if name not in ("$__CC4_XX", "$__CC3_X"): + cells.append([name + "_X", 12, XOR(E, expr)]) + +with open("techlibs/gatemate/lut_tree_cells.genlib", "w") as glf: + def mkGate(name, cost, expr, max_load=9999, block_delay = 10, fanout_delay = 5): + name = name.replace(" ", "") + expr = expr.map() + + phase = "UNKNOWN" + if expr.isInv(): phase = "INV" + if expr.isNonInv(): phase = "NONINV" + + print("", file=glf) + print("GATE %s %d Y=%s;" % (name, cost, expr.as_genlib_term()), file=glf) + print("PIN * %s 1 %d %d %d %d %d" % (phase, max_load, block_delay, fanout_delay, block_delay, fanout_delay), file=glf) + print("GATE $__ZERO 0 Y=CONST0;", file=glf) + print("GATE $__ONE 0 Y=CONST1;", file=glf) + for name, cost, expr in cells: + mkGate(name, cost, expr) + +class LUTTreeNode: + def __init__(self, name, width, inputs=None): + self.name = name + self.width = width + self.inputs = inputs + def is_input(self): + return self.width == 0 + def map(self, expr, params, ports): + if self.is_input(): + # Input to LUT tree + if expr is None: + ports[self.name] = "" # disconnected input + else: + assert(len(expr.args) == 0) + ports[self.name] = expr.fun + return + if expr is None: + # Unused part of tree + params[self.name] = "4'b0000" + for i in self.inputs: + i.map(None, params, ports) + return + elif len(expr.args) == 0: + # Input to the expression; but not LUT tree + # Insert a route through + params[self.name] = "4'b1010" + self.inputs[0].map(expr, params, ports) + for i in self.inputs[1:]: + i.map(None, params, ports) + return + # Map uphill LUTs; uninverting arguments and keeping track of that if needed + arg_inv = [] + for (i, arg) in zip(self.inputs, expr.args): + if arg.fun == "NOT": + i.map(arg.args[0], params, ports) + arg_inv.append(True) + else: + i.map(arg, params, ports) + arg_inv.append(False) + # Determine base init value + assert self.width == 2 + if expr.fun == "AND": + init = 0b1000 + elif expr.fun == "OR": + init = 0b1110 + elif expr.fun == "XOR": + init = 0b0110 + else: + assert False, expr.fun + # Swap bits if init inverted + swapped_init = 0b0000 + for b in range(4): + if ((init >> b) & 0x1) == 0: continue + for i in range(2): + if arg_inv[i]: + b ^= (1 << i) + swapped_init |= (1 << b) + # Set init param + params[self.name] = "4'b{:04b}".format(swapped_init) + +def LUT2(name, i0, i1): return LUTTreeNode(name, 2, [i0, i1]) +def I(name): return LUTTreeNode(name, 0) + +lut_prims = { + "CC_LUT2": LUT2("INIT", I("I0"), I("I1")), + "CC_L2T4": LUT2( + "INIT_L10", + LUT2("INIT_L00", I("I0"), I("I1")), + LUT2("INIT_L01", I("I2"), I("I3")), + ), + "CC_L2T5": LUT2( + "INIT_L20", I("I4"), LUT2("INIT_L11", + LUT2("INIT_L02", I("I0"), I("I1")), + LUT2("INIT_L03", I("I2"), I("I3")), + ) + ) +} + +with open("techlibs/gatemate/lut_tree_map.v", "w") as vf: + # Non-automatic rules + print(""" +module \\$__ZERO (output Y); assign Y = 1'b0; endmodule +module \\$__ONE (output Y); assign Y = 1'b1; endmodule + +module \\$__CC_BUF (input A, output Y); assign Y = A; endmodule + +module \\$__CC_MUX (input A, B, C, output Y); + CC_MX2 _TECHMAP_REPLACE_ ( + .D0(A), .D1(B), .S0(C), + .Y(Y) + ); +endmodule +""", file=vf) + for name, cost, expr in cells: + expr = expr.mapMux().mapNot() # Don't map XOR + if name in ("$__CC_BUF", "$__CC_NOT", "$__CC_MUX"): + # Special cases + continue + if name.startswith("$__CC2_"): + prim = "CC_LUT2" + elif name.startswith("$__CC5_") or (name.startswith("$__CC4_") and cost == 12): + prim = "CC_L2T5" + else: + prim = "CC_L2T4" + ports = {} + params = {} + lut_prims[prim].map(expr, params, ports) + print("", file=vf) + print("module \\%s (input %s, output Y);" % (name, + ", ".join(sorted(set(x for x in ports.values() if x)))), file=vf) + print(" %s #(" % prim, file=vf) + for k, v in sorted(params.items(), key=lambda x: x[0]): + print(" .%s(%s)," % (k, v), file=vf) + print(" ) _TECHMAP_REPLACE_ (", file=vf) + print(" %s," % ", ".join(".%s(%s)" % (k, v) for k, v in sorted(ports.items(), key=lambda x:x[0])), + file=vf) + print(" .O(Y)", file=vf) + print(" );", file=vf) + print("endmodule", file=vf) diff --git a/techlibs/gatemate/synth_gatemate.cc b/techlibs/gatemate/synth_gatemate.cc index 93b16b2e0..dd4fde643 100644 --- a/techlibs/gatemate/synth_gatemate.cc +++ b/techlibs/gatemate/synth_gatemate.cc @@ -67,7 +67,10 @@ struct SynthGateMatePass : public ScriptPass log("\n");
log(" -nomx8, -nomx4\n");
log(" do not use CC_MX{8,4} multiplexer cells in output netlist.\n");
- log("\n");;
+ log("\n");
+ log(" -luttree\n");
+ log(" use new LUT tree mapping approach (EXPERIMENTAL).\n");
+ log("\n");
log(" -dff\n");
log(" run 'abc' with -dff option\n");
log("\n");
@@ -87,7 +90,7 @@ struct SynthGateMatePass : public ScriptPass }
string top_opt, vlog_file, json_file;
- bool noflatten, nobram, noaddf, nomult, nomx4, nomx8, dff, retime, noiopad, noclkbuf;
+ bool noflatten, nobram, noaddf, nomult, nomx4, nomx8, luttree, dff, retime, noiopad, noclkbuf;
void clear_flags() override
{
@@ -100,6 +103,7 @@ struct SynthGateMatePass : public ScriptPass nomult = false;
nomx4 = false;
nomx8 = false;
+ luttree = false;
dff = false;
retime = false;
noiopad = false;
@@ -158,6 +162,10 @@ struct SynthGateMatePass : public ScriptPass nomx8 = true;
continue;
}
+ if (args[argidx] == "-luttree") {
+ luttree = true;
+ continue;
+ }
if (args[argidx] == "-dff") {
dff = true;
continue;
@@ -298,11 +306,23 @@ struct SynthGateMatePass : public ScriptPass if (check_label("map_luts"))
{
- std::string abc_args = " -dress -lut 4";
- if (dff) {
- abc_args += " -dff";
+ if (luttree || help_mode) {
+ std::string abc_args = " -genlib +/gatemate/lut_tree_cells.genlib";
+ if (dff) {
+ abc_args += " -dff";
+ }
+ run("abc " + abc_args, "(with -luttree)");
+ run("techmap -map +/gatemate/lut_tree_map.v", "(with -luttree)");
+ run("gatemate_foldinv", "(with -luttree)");
+ run("techmap -map +/gatemate/inv_map.v", "(with -luttree)");
+ }
+ if (!luttree || help_mode) {
+ std::string abc_args = " -dress -lut 4";
+ if (dff) {
+ abc_args += " -dff";
+ }
+ run("abc " + abc_args, "(without -luttree)");
}
- run("abc " + abc_args);
run("clean");
}
diff --git a/techlibs/gowin/synth_gowin.cc b/techlibs/gowin/synth_gowin.cc index 15a0c41e0..0dffdf498 100644 --- a/techlibs/gowin/synth_gowin.cc +++ b/techlibs/gowin/synth_gowin.cc @@ -131,7 +131,6 @@ struct SynthGowinPass : public ScriptPass if (args[argidx] == "-json" && argidx+1 < args.size()) { json_file = args[++argidx]; nobram = true; - nolutram = true; continue; } if (args[argidx] == "-run" && argidx+1 < args.size()) { diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v index 2e1c6807a..52e8e2e3a 100644 --- a/techlibs/ice40/cells_sim.v +++ b/techlibs/ice40/cells_sim.v @@ -302,7 +302,9 @@ endmodule (* abc9_flop, lib_whitebox *) module SB_DFFE ( output reg Q, - input C, E `ICE40_DEFAULT_ASSIGNMENT_1, D + input C, + input E `ICE40_DEFAULT_ASSIGNMENT_1, + input D ); `SB_DFF_INIT @@ -589,7 +591,10 @@ endmodule (* abc9_flop, lib_whitebox *) module SB_DFFESR ( output reg Q, - input C, E `ICE40_DEFAULT_ASSIGNMENT_1, R, D + input C, + input E `ICE40_DEFAULT_ASSIGNMENT_1, + input R, + input D ); `SB_DFF_INIT @@ -647,7 +652,10 @@ endmodule (* abc9_box, lib_whitebox *) module SB_DFFER ( output reg Q, - input C, E `ICE40_DEFAULT_ASSIGNMENT_1, R, D + input C, + input E `ICE40_DEFAULT_ASSIGNMENT_1, + input R, + input D ); `SB_DFF_INIT @@ -724,7 +732,10 @@ endmodule (* abc9_flop, lib_whitebox *) module SB_DFFESS ( output reg Q, - input C, E `ICE40_DEFAULT_ASSIGNMENT_1, S, D + input C, + input E `ICE40_DEFAULT_ASSIGNMENT_1, + input S, + input D ); `SB_DFF_INIT @@ -782,7 +793,10 @@ endmodule (* abc9_box, lib_whitebox *) module SB_DFFES ( output reg Q, - input C, E `ICE40_DEFAULT_ASSIGNMENT_1, S, D + input C, + input E `ICE40_DEFAULT_ASSIGNMENT_1, + input S, + input D ); `SB_DFF_INIT @@ -899,7 +913,9 @@ endmodule (* abc9_flop, lib_whitebox *) module SB_DFFNE ( output reg Q, - input C, E `ICE40_DEFAULT_ASSIGNMENT_1, D + input C, + input E `ICE40_DEFAULT_ASSIGNMENT_1, + input D ); `SB_DFF_INIT @@ -1186,7 +1202,10 @@ endmodule (* abc9_flop, lib_whitebox *) module SB_DFFNESR ( output reg Q, - input C, E `ICE40_DEFAULT_ASSIGNMENT_1, R, D + input C, + input E `ICE40_DEFAULT_ASSIGNMENT_1, + input R, + input D ); `SB_DFF_INIT @@ -1244,7 +1263,10 @@ endmodule (* abc9_box, lib_whitebox *) module SB_DFFNER ( output reg Q, - input C, E `ICE40_DEFAULT_ASSIGNMENT_1, R, D + input C, + input E `ICE40_DEFAULT_ASSIGNMENT_1, + input R, + input D ); `SB_DFF_INIT @@ -1321,7 +1343,10 @@ endmodule (* abc9_flop, lib_whitebox *) module SB_DFFNESS ( output reg Q, - input C, E `ICE40_DEFAULT_ASSIGNMENT_1, S, D + input C, + input E `ICE40_DEFAULT_ASSIGNMENT_1, + input S, + input D ); `SB_DFF_INIT @@ -1379,7 +1404,10 @@ endmodule (* abc9_box, lib_whitebox *) module SB_DFFNES ( output reg Q, - input C, E `ICE40_DEFAULT_ASSIGNMENT_1, S, D + input C, + input E `ICE40_DEFAULT_ASSIGNMENT_1, + input S, + input D ); `SB_DFF_INIT diff --git a/techlibs/nexus/brams_map.v b/techlibs/nexus/brams_map.v index 3cada2414..17306cded 100644 --- a/techlibs/nexus/brams_map.v +++ b/techlibs/nexus/brams_map.v @@ -32,10 +32,10 @@ output [PORT_B_WIDTH-1:0] PORT_B_RD_DATA; function [319:0] init_slice; input integer idx; - integer i, j; + integer i; init_slice = 0; - for (i = 0; i < 16; i = i + 1) begin - init_slice[i*20+:18] = INIT[(idx * 16 + i) * 18+:18]; + for (i = 0; i < 32; i = i + 1) begin + init_slice[i*10+:9] = INIT[(idx * 32 + i) * 9+:9]; end endfunction @@ -43,8 +43,28 @@ wire [17:0] DOA; wire [17:0] DOB; wire [17:0] DIA = PORT_A_WR_DATA; wire [17:0] DIB = PORT_B_WR_DATA; -wire [13:0] ADA = PORT_A_WIDTH == 18 ? {PORT_A_ADDR[13:2], PORT_A_WR_BE} : PORT_A_ADDR; -wire [13:0] ADB = PORT_B_WIDTH == 18 ? {PORT_B_ADDR[13:2], PORT_B_WR_BE} : PORT_B_ADDR; +wire [13:0] ADA; +wire [13:0] ADB; + +generate + +case(PORT_A_WIDTH) +1: assign ADA = PORT_A_ADDR; +2: assign ADA = {PORT_A_ADDR[13:1], 1'b1}; +4: assign ADA = {PORT_A_ADDR[13:2], 2'b11}; +9: assign ADA = {PORT_A_ADDR[13:3], 3'b111}; +18: assign ADA = {PORT_A_ADDR[13:4], 2'b11, PORT_A_WR_BE}; +endcase + +case(PORT_B_WIDTH) +1: assign ADB = PORT_B_ADDR; +2: assign ADB = {PORT_B_ADDR[13:1], 1'b1}; +4: assign ADB = {PORT_B_ADDR[13:2], 2'b11}; +9: assign ADB = {PORT_B_ADDR[13:3], 3'b111}; +18: assign ADB = {PORT_B_ADDR[13:4], 2'b11, PORT_B_WR_BE}; +endcase + +endgenerate assign PORT_A_RD_DATA = DOA; assign PORT_B_RD_DATA = DOB; @@ -122,8 +142,8 @@ DP16K #( .RESETMODE_B(PORT_B_OPTION_RESETMODE), .ASYNC_RST_RELEASE_A(PORT_A_OPTION_RESETMODE), .ASYNC_RST_RELEASE_B(PORT_B_OPTION_RESETMODE), - .CSDECODE_A("000"), - .CSDECODE_B("000"), + .CSDECODE_A("111"), + .CSDECODE_B("111"), .GSR("DISABLED"), ) _TECHMAP_REPLACE_ ( .CLKA(PORT_A_CLK), @@ -176,10 +196,10 @@ input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA; function [319:0] init_slice; input integer idx; - integer i, j; + integer i; init_slice = 0; - for (i = 0; i < 16; i = i + 1) begin - init_slice[i*20+:18] = INIT[(idx * 16 + i) * 18+:18]; + for (i = 0; i < 32; i = i + 1) begin + init_slice[i*10+:9] = INIT[(idx * 32 + i) * 9+:9]; end endfunction @@ -188,11 +208,29 @@ wire [35:0] DO; assign PORT_R_RD_DATA = DO; -wire [13:0] ADW = PORT_W_WIDTH == 36 ? {PORT_W_ADDR[13:4], PORT_W_WR_EN} : - (PORT_W_WIDTH == 18 ? {PORT_W_ADDR[13:2], PORT_W_WR_EN} : PORT_W_ADDR); +wire [13:0] ADW; +wire [13:0] ADR; generate +case (PORT_W_WIDTH) +1: assign ADW = PORT_W_ADDR; +2: assign ADW = {PORT_W_ADDR[13:1], 1'b1}; +4: assign ADW = {PORT_W_ADDR[13:2], 2'b11}; +9: assign ADW = {PORT_W_ADDR[13:3], 3'b111}; +18: assign ADW = {PORT_W_ADDR[13:4], 2'b11, PORT_W_WR_EN}; +36: assign ADW = {PORT_W_ADDR[13:5], 1'b1, PORT_W_WR_EN}; +endcase + +case (PORT_R_WIDTH) +1: assign ADR = PORT_R_ADDR; +2: assign ADR = {PORT_R_ADDR[13:1], 1'b1}; +4: assign ADR = {PORT_R_ADDR[13:2], 2'b11}; +9: assign ADR = {PORT_R_ADDR[13:3], 3'b111}; +18: assign ADR = {PORT_R_ADDR[13:4], 4'b1111}; +36: assign ADR = {PORT_R_ADDR[13:5], 5'b11111}; +endcase + if (OPTION_SAME_CLOCK) begin PDPSC16K #( @@ -265,8 +303,8 @@ PDPSC16K #( .OUTREG("BYPASSED"), .RESETMODE(PORT_R_OPTION_RESETMODE), .ASYNC_RST_RELEASE(PORT_R_OPTION_RESETMODE), - .CSDECODE_W("000"), - .CSDECODE_R("000"), + .CSDECODE_W("111"), + .CSDECODE_R("111"), .ECC("DISABLED"), .GSR("DISABLED"), ) _TECHMAP_REPLACE_ ( @@ -280,7 +318,7 @@ PDPSC16K #( .CER(PORT_R_CLK_EN), .RST(PORT_R_OPTION_RESETMODE == "SYNC" ? PORT_R_RD_SRST : PORT_R_RD_ARST), .CSR(3'b111), - .ADR(PORT_R_ADDR), + .ADR(ADR), .DO(DO), ); @@ -356,8 +394,8 @@ PDP16K #( .OUTREG("BYPASSED"), .RESETMODE(PORT_R_OPTION_RESETMODE), .ASYNC_RST_RELEASE(PORT_R_OPTION_RESETMODE), - .CSDECODE_W("000"), - .CSDECODE_R("000"), + .CSDECODE_W("111"), + .CSDECODE_R("111"), .ECC("DISABLED"), .GSR("DISABLED"), ) _TECHMAP_REPLACE_ ( @@ -371,7 +409,7 @@ PDP16K #( .CER(PORT_R_CLK_EN), .RST(PORT_R_OPTION_RESETMODE == "SYNC" ? PORT_R_RD_SRST : PORT_R_RD_ARST), .CSR(3'b111), - .ADR(PORT_R_ADDR), + .ADR(ADR), .DO(DO), ); diff --git a/tests/arch/gatemate/gen_luttrees.py b/tests/arch/gatemate/gen_luttrees.py new file mode 100644 index 000000000..27d2225a2 --- /dev/null +++ b/tests/arch/gatemate/gen_luttrees.py @@ -0,0 +1,48 @@ +from random import Random + +def main(): + r = Random(1) + + N = 750 + with open("tests/arch/gatemate/luttrees.v", "w") as v: + print(f"module luttrees(input [{N-1}:0] a, b, c, d, e, output [{N-1}:0] q);", file=v) + for i in range(N): + def f(): + return r.choice(["&", "|", "^"]) + + def a(x): + return f"({r.choice(['', '!'])}{x}[{i}])" + + # Bias towards testing bigger functions + k = r.choice([2, 3, 4, 4, 4, 5, 5, 5]) + if k == 2: + expr = f"{a('a')}{f()}{a('b')}" + elif k == 3: + expr = f"({a('a')}{f()}{a('b')}){f()}{a('c')}" + elif k == 4: + # Two types of 4-input function + if r.choice([False, True]): + expr = f"(({a('a')}{f()}{a('b')}){f()}{a('c')}){f()}{a('d')}" + else: + expr = f"({a('a')}{f()}{a('b')}){f()}({a('c')}{f()}{a('d')})" + elif k == 5: + expr = f"(({a('a')}{f()}{a('b')}){f()}({a('c')}{f()}{a('d')})){f()}{a('e')}" + print(f" assign q[{i}] = {expr};", file=v) + print("endmodule", file=v) + with open("tests/arch/gatemate/luttrees.ys", "w") as s: + print(f""" +read_verilog luttrees.v +design -save read + +hierarchy -top luttrees +proc +equiv_opt -async2sync -assert -map +/gatemate/cells_sim.v synth_gatemate -noiopad -luttree -nomx4 -nomx8 # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd luttrees # Constrain all select calls below inside the top module + +select -assert-count {N} t:CC_LUT2 t:CC_L2T4 t:CC_L2T5 %% +select -assert-none t:CC_LUT2 t:CC_L2T4 t:CC_L2T5 %% t:* %D +""", file=s) + +if __name__ == '__main__': + main() diff --git a/tests/arch/gatemate/luttrees.v b/tests/arch/gatemate/luttrees.v new file mode 100644 index 000000000..7fc02916d --- /dev/null +++ b/tests/arch/gatemate/luttrees.v @@ -0,0 +1,752 @@ +module luttrees(input [749:0] a, b, c, d, e, output [749:0] q); + assign q[0] = ((!a[0])&(!b[0]))|((!c[0])^(!d[0])); + assign q[1] = ((!a[1])&(!b[1]))|((c[1])^(!d[1])); + assign q[2] = ((a[2])|(b[2]))&((c[2])^(d[2])); + assign q[3] = (((a[3])|(b[3]))^((c[3])|(!d[3])))^(e[3]); + assign q[4] = (((a[4])^(b[4]))|((!c[4])&(!d[4])))^(e[4]); + assign q[5] = (((a[5])^(!b[5]))^(!c[5]))^(d[5]); + assign q[6] = (((!a[6])^(!b[6]))^(c[6]))|(d[6]); + assign q[7] = (((!a[7])^(b[7]))|((!c[7])&(!d[7])))^(e[7]); + assign q[8] = (((!a[8])|(b[8]))|(c[8]))|(!d[8]); + assign q[9] = ((a[9])&(b[9]))^((c[9])|(!d[9])); + assign q[10] = (((!a[10])|(b[10]))|((c[10])^(d[10])))|(e[10]); + assign q[11] = (((!a[11])^(b[11]))^((!c[11])|(!d[11])))|(!e[11]); + assign q[12] = (!a[12])|(b[12]); + assign q[13] = ((a[13])&(!b[13]))&((c[13])&(d[13])); + assign q[14] = (((a[14])|(b[14]))|((c[14])^(d[14])))|(!e[14]); + assign q[15] = ((a[15])&(!b[15]))^(c[15]); + assign q[16] = (((!a[16])^(!b[16]))|(!c[16]))&(d[16]); + assign q[17] = (((!a[17])|(b[17]))|(c[17]))|(d[17]); + assign q[18] = (((a[18])&(b[18]))|((c[18])&(d[18])))|(!e[18]); + assign q[19] = (((a[19])^(b[19]))|(!c[19]))^(!d[19]); + assign q[20] = (!a[20])&(b[20]); + assign q[21] = (!a[21])&(b[21]); + assign q[22] = (((a[22])|(!b[22]))&(c[22]))^(d[22]); + assign q[23] = (((a[23])^(b[23]))|(c[23]))|(d[23]); + assign q[24] = (((a[24])|(b[24]))^(!c[24]))|(!d[24]); + assign q[25] = (!a[25])^(!b[25]); + assign q[26] = ((a[26])&(!b[26]))^((c[26])|(!d[26])); + assign q[27] = (((a[27])|(!b[27]))^(!c[27]))^(d[27]); + assign q[28] = ((a[28])&(b[28]))&(c[28]); + assign q[29] = (((!a[29])^(!b[29]))|(!c[29]))|(d[29]); + assign q[30] = ((!a[30])&(b[30]))|((c[30])|(d[30])); + assign q[31] = (((a[31])&(!b[31]))&((!c[31])&(d[31])))^(e[31]); + assign q[32] = (((!a[32])^(b[32]))|(!c[32]))&(d[32]); + assign q[33] = ((a[33])&(!b[33]))&((c[33])&(d[33])); + assign q[34] = (((a[34])&(!b[34]))&((c[34])&(d[34])))|(!e[34]); + assign q[35] = (((!a[35])|(b[35]))&(!c[35]))&(d[35]); + assign q[36] = (!a[36])^(!b[36]); + assign q[37] = (((!a[37])|(!b[37]))&((c[37])|(!d[37])))&(!e[37]); + assign q[38] = (((!a[38])|(b[38]))^(c[38]))|(d[38]); + assign q[39] = (((a[39])|(b[39]))|(c[39]))^(!d[39]); + assign q[40] = (((!a[40])&(!b[40]))&(!c[40]))^(!d[40]); + assign q[41] = (((a[41])^(b[41]))&(c[41]))&(d[41]); + assign q[42] = (((a[42])|(b[42]))^((c[42])&(d[42])))|(!e[42]); + assign q[43] = (((!a[43])&(b[43]))^((!c[43])&(d[43])))&(e[43]); + assign q[44] = (((!a[44])&(!b[44]))&(c[44]))&(d[44]); + assign q[45] = (((a[45])&(!b[45]))|((c[45])&(d[45])))|(e[45]); + assign q[46] = (((!a[46])^(!b[46]))^((!c[46])^(!d[46])))&(!e[46]); + assign q[47] = (((a[47])|(!b[47]))&((!c[47])^(d[47])))&(!e[47]); + assign q[48] = ((a[48])|(!b[48]))|((!c[48])&(d[48])); + assign q[49] = (((a[49])&(!b[49]))^(!c[49]))^(d[49]); + assign q[50] = (((!a[50])^(!b[50]))&(!c[50]))|(!d[50]); + assign q[51] = ((a[51])^(!b[51]))&((c[51])|(!d[51])); + assign q[52] = (((a[52])^(!b[52]))^(c[52]))&(!d[52]); + assign q[53] = ((!a[53])|(b[53]))^((c[53])|(!d[53])); + assign q[54] = (((!a[54])^(b[54]))^((c[54])^(d[54])))|(e[54]); + assign q[55] = ((a[55])^(b[55]))|((c[55])|(!d[55])); + assign q[56] = (((a[56])|(!b[56]))&((!c[56])&(d[56])))|(!e[56]); + assign q[57] = ((!a[57])|(b[57]))|(c[57]); + assign q[58] = (((a[58])&(b[58]))|(c[58]))&(!d[58]); + assign q[59] = ((!a[59])|(!b[59]))^((!c[59])&(!d[59])); + assign q[60] = (((!a[60])&(b[60]))^((!c[60])&(!d[60])))&(e[60]); + assign q[61] = ((a[61])^(!b[61]))^(c[61]); + assign q[62] = ((!a[62])^(!b[62]))|(!c[62]); + assign q[63] = (((a[63])&(!b[63]))^((!c[63])|(!d[63])))^(!e[63]); + assign q[64] = (((!a[64])&(!b[64]))|((c[64])^(d[64])))|(e[64]); + assign q[65] = (((!a[65])^(!b[65]))^((c[65])|(d[65])))|(e[65]); + assign q[66] = (((!a[66])|(!b[66]))^((c[66])|(d[66])))^(!e[66]); + assign q[67] = (!a[67])^(b[67]); + assign q[68] = (((!a[68])&(b[68]))^((c[68])|(!d[68])))^(!e[68]); + assign q[69] = ((!a[69])|(!b[69]))&((!c[69])^(d[69])); + assign q[70] = ((!a[70])&(!b[70]))&((!c[70])&(d[70])); + assign q[71] = ((!a[71])^(!b[71]))^((!c[71])&(d[71])); + assign q[72] = (((!a[72])&(b[72]))^(!c[72]))|(d[72]); + assign q[73] = (((!a[73])|(b[73]))|(!c[73]))&(!d[73]); + assign q[74] = (((a[74])|(b[74]))^(c[74]))^(!d[74]); + assign q[75] = (((!a[75])&(!b[75]))&((c[75])^(d[75])))^(!e[75]); + assign q[76] = (((!a[76])^(b[76]))&(c[76]))^(!d[76]); + assign q[77] = (((!a[77])|(b[77]))&((c[77])^(!d[77])))&(e[77]); + assign q[78] = (((!a[78])|(b[78]))&(c[78]))&(d[78]); + assign q[79] = ((!a[79])^(!b[79]))|((!c[79])&(d[79])); + assign q[80] = ((a[80])|(b[80]))|(!c[80]); + assign q[81] = (((a[81])&(b[81]))|((!c[81])^(!d[81])))&(!e[81]); + assign q[82] = (((!a[82])^(b[82]))&(c[82]))&(d[82]); + assign q[83] = (!a[83])|(!b[83]); + assign q[84] = ((!a[84])&(b[84]))&((c[84])|(d[84])); + assign q[85] = (!a[85])|(b[85]); + assign q[86] = ((!a[86])^(!b[86]))&(c[86]); + assign q[87] = (a[87])&(!b[87]); + assign q[88] = ((!a[88])&(b[88]))&(!c[88]); + assign q[89] = (((a[89])^(!b[89]))|(!c[89]))^(!d[89]); + assign q[90] = ((a[90])&(b[90]))|((!c[90])^(d[90])); + assign q[91] = (((!a[91])^(b[91]))^((!c[91])^(d[91])))^(!e[91]); + assign q[92] = ((!a[92])&(b[92]))&(c[92]); + assign q[93] = (((a[93])&(b[93]))^(!c[93]))^(!d[93]); + assign q[94] = ((!a[94])&(b[94]))^(c[94]); + assign q[95] = (((a[95])|(!b[95]))&((!c[95])&(!d[95])))|(e[95]); + assign q[96] = ((!a[96])&(!b[96]))|((c[96])|(!d[96])); + assign q[97] = ((!a[97])&(!b[97]))&(c[97]); + assign q[98] = (((!a[98])^(!b[98]))^((!c[98])&(d[98])))^(!e[98]); + assign q[99] = (!a[99])^(b[99]); + assign q[100] = (((!a[100])^(!b[100]))&(!c[100]))|(d[100]); + assign q[101] = ((a[101])|(!b[101]))|(!c[101]); + assign q[102] = (((!a[102])|(!b[102]))^((c[102])^(d[102])))&(!e[102]); + assign q[103] = (((!a[103])^(b[103]))|((!c[103])|(!d[103])))|(!e[103]); + assign q[104] = ((a[104])&(b[104]))^(!c[104]); + assign q[105] = ((!a[105])|(!b[105]))|((!c[105])|(!d[105])); + assign q[106] = ((a[106])|(b[106]))^((c[106])&(d[106])); + assign q[107] = (((a[107])&(!b[107]))^((c[107])&(!d[107])))&(e[107]); + assign q[108] = ((a[108])^(b[108]))^(!c[108]); + assign q[109] = (!a[109])|(!b[109]); + assign q[110] = ((!a[110])&(!b[110]))|((!c[110])^(d[110])); + assign q[111] = (((a[111])|(!b[111]))&((c[111])^(!d[111])))^(!e[111]); + assign q[112] = ((!a[112])^(!b[112]))&(!c[112]); + assign q[113] = (((a[113])&(!b[113]))^((c[113])^(d[113])))|(!e[113]); + assign q[114] = (((!a[114])^(!b[114]))|(!c[114]))&(!d[114]); + assign q[115] = ((!a[115])^(b[115]))|((c[115])|(!d[115])); + assign q[116] = (((!a[116])^(b[116]))&((c[116])&(!d[116])))|(!e[116]); + assign q[117] = (((!a[117])|(!b[117]))|(c[117]))|(!d[117]); + assign q[118] = (((a[118])&(b[118]))|(!c[118]))&(!d[118]); + assign q[119] = (((!a[119])^(!b[119]))^((!c[119])^(!d[119])))|(!e[119]); + assign q[120] = (((a[120])^(!b[120]))|((!c[120])&(d[120])))&(e[120]); + assign q[121] = ((a[121])&(!b[121]))&((!c[121])&(!d[121])); + assign q[122] = ((a[122])|(b[122]))^((c[122])^(!d[122])); + assign q[123] = (((a[123])^(b[123]))^((!c[123])^(d[123])))|(!e[123]); + assign q[124] = (((a[124])&(b[124]))|(c[124]))^(!d[124]); + assign q[125] = (((!a[125])&(!b[125]))&(c[125]))|(d[125]); + assign q[126] = (((!a[126])^(!b[126]))|(c[126]))^(!d[126]); + assign q[127] = (((a[127])&(!b[127]))^((!c[127])&(!d[127])))|(e[127]); + assign q[128] = (((!a[128])^(b[128]))|(c[128]))&(!d[128]); + assign q[129] = (((a[129])^(b[129]))|(!c[129]))|(!d[129]); + assign q[130] = (((!a[130])&(!b[130]))|(!c[130]))&(!d[130]); + assign q[131] = ((!a[131])^(b[131]))&((!c[131])^(d[131])); + assign q[132] = (((!a[132])|(!b[132]))&(c[132]))&(!d[132]); + assign q[133] = ((a[133])^(b[133]))^((c[133])&(d[133])); + assign q[134] = (((!a[134])&(b[134]))|(c[134]))&(d[134]); + assign q[135] = (((!a[135])^(!b[135]))&(c[135]))|(!d[135]); + assign q[136] = ((!a[136])&(b[136]))|((c[136])&(d[136])); + assign q[137] = (((a[137])|(b[137]))|(c[137]))&(d[137]); + assign q[138] = (((!a[138])^(b[138]))&((!c[138])|(d[138])))^(!e[138]); + assign q[139] = ((!a[139])^(b[139]))^(!c[139]); + assign q[140] = (((a[140])^(!b[140]))^((!c[140])^(!d[140])))|(e[140]); + assign q[141] = ((a[141])^(b[141]))&((c[141])&(!d[141])); + assign q[142] = ((!a[142])^(!b[142]))^((!c[142])|(d[142])); + assign q[143] = (((a[143])^(!b[143]))&((!c[143])^(d[143])))^(!e[143]); + assign q[144] = (((a[144])&(b[144]))^((c[144])^(d[144])))|(!e[144]); + assign q[145] = (((!a[145])&(b[145]))|((!c[145])|(d[145])))^(e[145]); + assign q[146] = (((a[146])^(b[146]))^(c[146]))|(d[146]); + assign q[147] = (((a[147])|(!b[147]))|((!c[147])|(!d[147])))^(!e[147]); + assign q[148] = (a[148])|(b[148]); + assign q[149] = (a[149])&(b[149]); + assign q[150] = (((!a[150])^(!b[150]))|((c[150])^(d[150])))&(!e[150]); + assign q[151] = ((a[151])^(!b[151]))|((!c[151])|(d[151])); + assign q[152] = (((!a[152])|(!b[152]))|((!c[152])|(d[152])))^(e[152]); + assign q[153] = (!a[153])^(b[153]); + assign q[154] = (((!a[154])^(b[154]))^(c[154]))|(d[154]); + assign q[155] = (((a[155])|(b[155]))&((c[155])|(!d[155])))^(e[155]); + assign q[156] = (((!a[156])|(!b[156]))^((c[156])|(!d[156])))|(e[156]); + assign q[157] = (((!a[157])&(b[157]))&((!c[157])|(d[157])))&(e[157]); + assign q[158] = (((!a[158])^(!b[158]))&(c[158]))^(d[158]); + assign q[159] = ((!a[159])|(b[159]))^((!c[159])&(d[159])); + assign q[160] = (((a[160])^(b[160]))|((c[160])&(d[160])))&(!e[160]); + assign q[161] = (a[161])|(b[161]); + assign q[162] = (a[162])^(b[162]); + assign q[163] = (((!a[163])&(b[163]))|(c[163]))^(d[163]); + assign q[164] = (((a[164])^(b[164]))&((c[164])|(!d[164])))|(!e[164]); + assign q[165] = ((!a[165])^(!b[165]))^((!c[165])^(d[165])); + assign q[166] = (((!a[166])&(!b[166]))|((!c[166])&(!d[166])))^(!e[166]); + assign q[167] = ((!a[167])|(b[167]))|((!c[167])|(!d[167])); + assign q[168] = (((!a[168])^(!b[168]))&((c[168])&(!d[168])))&(e[168]); + assign q[169] = ((a[169])^(!b[169]))^(c[169]); + assign q[170] = (((!a[170])^(b[170]))&(c[170]))&(d[170]); + assign q[171] = (!a[171])|(!b[171]); + assign q[172] = ((a[172])&(!b[172]))&((!c[172])&(!d[172])); + assign q[173] = (((a[173])&(!b[173]))^((c[173])|(!d[173])))^(!e[173]); + assign q[174] = ((!a[174])&(!b[174]))&(!c[174]); + assign q[175] = ((!a[175])|(!b[175]))^(c[175]); + assign q[176] = (!a[176])&(b[176]); + assign q[177] = ((a[177])|(b[177]))^(c[177]); + assign q[178] = (((a[178])^(b[178]))|((c[178])^(!d[178])))^(e[178]); + assign q[179] = ((a[179])^(!b[179]))^(!c[179]); + assign q[180] = (((a[180])^(b[180]))^(c[180]))|(!d[180]); + assign q[181] = (((!a[181])|(!b[181]))&((c[181])&(d[181])))|(e[181]); + assign q[182] = (((a[182])|(!b[182]))&((c[182])&(d[182])))|(!e[182]); + assign q[183] = ((a[183])^(!b[183]))^((!c[183])&(d[183])); + assign q[184] = (((a[184])|(b[184]))|((c[184])^(d[184])))&(!e[184]); + assign q[185] = (((a[185])^(b[185]))^((!c[185])&(!d[185])))^(!e[185]); + assign q[186] = (((!a[186])|(!b[186]))&((!c[186])&(!d[186])))|(!e[186]); + assign q[187] = (((a[187])^(!b[187]))|(!c[187]))^(d[187]); + assign q[188] = ((a[188])^(b[188]))&(!c[188]); + assign q[189] = (((!a[189])^(b[189]))^((!c[189])^(d[189])))|(e[189]); + assign q[190] = (((!a[190])^(b[190]))&((!c[190])|(d[190])))&(e[190]); + assign q[191] = ((!a[191])|(!b[191]))|(!c[191]); + assign q[192] = (((a[192])^(b[192]))^(c[192]))|(!d[192]); + assign q[193] = ((a[193])^(b[193]))|((!c[193])&(d[193])); + assign q[194] = (((a[194])&(b[194]))|(!c[194]))&(!d[194]); + assign q[195] = ((!a[195])|(!b[195]))^((c[195])&(!d[195])); + assign q[196] = (((a[196])&(!b[196]))|((c[196])^(d[196])))^(!e[196]); + assign q[197] = ((a[197])&(b[197]))^((c[197])^(!d[197])); + assign q[198] = (((a[198])&(!b[198]))|((!c[198])^(!d[198])))&(e[198]); + assign q[199] = (!a[199])&(b[199]); + assign q[200] = ((a[200])&(b[200]))^((c[200])&(d[200])); + assign q[201] = (((!a[201])&(!b[201]))^(!c[201]))|(!d[201]); + assign q[202] = ((a[202])^(b[202]))&(!c[202]); + assign q[203] = (((!a[203])|(b[203]))|((c[203])&(!d[203])))^(!e[203]); + assign q[204] = (((a[204])^(!b[204]))&((!c[204])^(!d[204])))^(!e[204]); + assign q[205] = (((a[205])|(!b[205]))^((!c[205])^(!d[205])))|(!e[205]); + assign q[206] = (a[206])|(!b[206]); + assign q[207] = (((a[207])^(!b[207]))&((!c[207])|(d[207])))|(!e[207]); + assign q[208] = (a[208])|(b[208]); + assign q[209] = ((!a[209])|(b[209]))|((!c[209])|(d[209])); + assign q[210] = ((!a[210])&(b[210]))&((!c[210])|(!d[210])); + assign q[211] = ((a[211])^(!b[211]))|((!c[211])|(!d[211])); + assign q[212] = (((a[212])&(!b[212]))&((c[212])^(d[212])))^(e[212]); + assign q[213] = (((!a[213])&(b[213]))^(!c[213]))^(!d[213]); + assign q[214] = (!a[214])|(!b[214]); + assign q[215] = ((a[215])|(!b[215]))^((!c[215])^(!d[215])); + assign q[216] = (((a[216])^(!b[216]))^((!c[216])|(d[216])))|(e[216]); + assign q[217] = (((a[217])^(b[217]))^((!c[217])|(!d[217])))&(e[217]); + assign q[218] = ((a[218])&(!b[218]))&(c[218]); + assign q[219] = ((a[219])|(b[219]))&((c[219])&(d[219])); + assign q[220] = (((!a[220])&(b[220]))^((c[220])|(d[220])))|(!e[220]); + assign q[221] = ((a[221])&(!b[221]))&((c[221])^(!d[221])); + assign q[222] = (!a[222])|(!b[222]); + assign q[223] = ((a[223])^(b[223]))^(c[223]); + assign q[224] = ((a[224])&(!b[224]))&((c[224])|(d[224])); + assign q[225] = ((!a[225])&(b[225]))&(!c[225]); + assign q[226] = (((a[226])^(b[226]))|((c[226])&(d[226])))&(e[226]); + assign q[227] = ((a[227])&(b[227]))|((!c[227])^(!d[227])); + assign q[228] = ((!a[228])&(!b[228]))|((!c[228])|(!d[228])); + assign q[229] = (((a[229])|(b[229]))|((!c[229])&(d[229])))&(e[229]); + assign q[230] = (((!a[230])^(!b[230]))|((!c[230])|(!d[230])))|(!e[230]); + assign q[231] = (((!a[231])|(b[231]))|((c[231])|(d[231])))|(e[231]); + assign q[232] = ((!a[232])^(b[232]))&((c[232])&(!d[232])); + assign q[233] = (((!a[233])&(!b[233]))|(!c[233]))&(!d[233]); + assign q[234] = (((!a[234])&(b[234]))^(!c[234]))&(d[234]); + assign q[235] = (((a[235])&(b[235]))^((!c[235])^(!d[235])))^(!e[235]); + assign q[236] = (((!a[236])^(b[236]))&((!c[236])&(!d[236])))^(e[236]); + assign q[237] = (a[237])|(!b[237]); + assign q[238] = (((a[238])|(b[238]))&((!c[238])^(d[238])))&(e[238]); + assign q[239] = ((!a[239])^(!b[239]))&((c[239])&(d[239])); + assign q[240] = (((!a[240])^(!b[240]))&(!c[240]))&(!d[240]); + assign q[241] = ((!a[241])&(b[241]))&(c[241]); + assign q[242] = ((a[242])|(!b[242]))&((c[242])|(d[242])); + assign q[243] = ((a[243])|(b[243]))^((!c[243])&(d[243])); + assign q[244] = (((!a[244])^(b[244]))&(!c[244]))|(!d[244]); + assign q[245] = (((a[245])^(!b[245]))^(!c[245]))^(!d[245]); + assign q[246] = ((!a[246])|(!b[246]))&(!c[246]); + assign q[247] = ((!a[247])&(b[247]))|(c[247]); + assign q[248] = (((!a[248])|(!b[248]))|((!c[248])&(!d[248])))&(!e[248]); + assign q[249] = (a[249])|(!b[249]); + assign q[250] = (((!a[250])^(!b[250]))^(!c[250]))^(!d[250]); + assign q[251] = (((a[251])|(b[251]))|(!c[251]))^(d[251]); + assign q[252] = (((!a[252])&(!b[252]))^((c[252])&(d[252])))&(e[252]); + assign q[253] = (!a[253])^(!b[253]); + assign q[254] = (((a[254])^(!b[254]))|((c[254])|(!d[254])))&(!e[254]); + assign q[255] = (!a[255])|(!b[255]); + assign q[256] = (((a[256])|(!b[256]))&(c[256]))&(!d[256]); + assign q[257] = (!a[257])^(!b[257]); + assign q[258] = (((!a[258])&(b[258]))^(c[258]))|(d[258]); + assign q[259] = (((!a[259])&(!b[259]))|(c[259]))&(!d[259]); + assign q[260] = ((!a[260])&(!b[260]))|((!c[260])^(!d[260])); + assign q[261] = (((a[261])|(b[261]))^(c[261]))|(d[261]); + assign q[262] = (((!a[262])^(b[262]))^((!c[262])|(!d[262])))|(e[262]); + assign q[263] = (a[263])^(b[263]); + assign q[264] = ((!a[264])&(!b[264]))|(c[264]); + assign q[265] = (((!a[265])^(b[265]))^((c[265])^(!d[265])))|(!e[265]); + assign q[266] = (((a[266])^(!b[266]))&(c[266]))^(d[266]); + assign q[267] = ((a[267])|(!b[267]))|(!c[267]); + assign q[268] = ((!a[268])&(!b[268]))|(!c[268]); + assign q[269] = (((!a[269])^(b[269]))&((!c[269])^(!d[269])))^(!e[269]); + assign q[270] = (((a[270])|(b[270]))^(c[270]))&(d[270]); + assign q[271] = (((a[271])&(b[271]))|((c[271])|(d[271])))|(!e[271]); + assign q[272] = (((a[272])|(!b[272]))^(!c[272]))|(!d[272]); + assign q[273] = (((!a[273])|(b[273]))&((!c[273])&(!d[273])))&(!e[273]); + assign q[274] = ((a[274])^(!b[274]))|(!c[274]); + assign q[275] = (((!a[275])^(!b[275]))|((!c[275])^(!d[275])))^(e[275]); + assign q[276] = ((a[276])^(b[276]))&((!c[276])^(d[276])); + assign q[277] = (((!a[277])&(!b[277]))|((!c[277])^(!d[277])))&(!e[277]); + assign q[278] = (((!a[278])^(!b[278]))^(c[278]))&(!d[278]); + assign q[279] = (((a[279])&(b[279]))^((!c[279])|(d[279])))^(!e[279]); + assign q[280] = ((!a[280])&(!b[280]))&(!c[280]); + assign q[281] = ((a[281])|(b[281]))|((!c[281])|(!d[281])); + assign q[282] = ((a[282])&(b[282]))&((c[282])|(d[282])); + assign q[283] = (((a[283])|(!b[283]))^((c[283])&(!d[283])))&(e[283]); + assign q[284] = (((!a[284])&(b[284]))&(!c[284]))^(d[284]); + assign q[285] = (((a[285])|(!b[285]))&(!c[285]))|(d[285]); + assign q[286] = (((a[286])^(b[286]))&((c[286])^(!d[286])))&(!e[286]); + assign q[287] = ((a[287])&(!b[287]))|((c[287])|(!d[287])); + assign q[288] = ((!a[288])|(!b[288]))|((!c[288])|(!d[288])); + assign q[289] = ((a[289])&(b[289]))&((c[289])&(!d[289])); + assign q[290] = (((!a[290])^(b[290]))&((c[290])^(d[290])))&(!e[290]); + assign q[291] = (((a[291])&(!b[291]))|(!c[291]))^(d[291]); + assign q[292] = (((a[292])^(!b[292]))^((c[292])|(d[292])))&(e[292]); + assign q[293] = (((a[293])&(!b[293]))&((c[293])|(d[293])))|(e[293]); + assign q[294] = (((a[294])^(!b[294]))&((!c[294])^(d[294])))&(!e[294]); + assign q[295] = (((a[295])^(!b[295]))|((!c[295])|(d[295])))&(e[295]); + assign q[296] = (((a[296])&(!b[296]))&((c[296])&(d[296])))&(!e[296]); + assign q[297] = ((!a[297])|(b[297]))^((!c[297])^(!d[297])); + assign q[298] = (((a[298])^(b[298]))|((!c[298])|(d[298])))|(!e[298]); + assign q[299] = (((!a[299])^(b[299]))|((c[299])^(!d[299])))|(!e[299]); + assign q[300] = (((!a[300])^(!b[300]))^(!c[300]))|(d[300]); + assign q[301] = ((a[301])&(!b[301]))&((!c[301])|(!d[301])); + assign q[302] = ((a[302])^(b[302]))|((c[302])|(!d[302])); + assign q[303] = (((!a[303])&(b[303]))^((c[303])|(d[303])))|(!e[303]); + assign q[304] = (!a[304])^(b[304]); + assign q[305] = (((a[305])&(!b[305]))^((!c[305])&(!d[305])))^(!e[305]); + assign q[306] = (a[306])&(b[306]); + assign q[307] = ((a[307])&(!b[307]))&(c[307]); + assign q[308] = ((a[308])&(!b[308]))^((!c[308])|(d[308])); + assign q[309] = ((a[309])^(!b[309]))&((c[309])|(!d[309])); + assign q[310] = (((!a[310])|(b[310]))&((c[310])^(d[310])))^(!e[310]); + assign q[311] = (((!a[311])|(b[311]))&((c[311])&(d[311])))|(e[311]); + assign q[312] = (((!a[312])&(b[312]))|((c[312])^(!d[312])))&(!e[312]); + assign q[313] = (!a[313])|(!b[313]); + assign q[314] = ((a[314])^(!b[314]))^((c[314])|(!d[314])); + assign q[315] = (a[315])&(!b[315]); + assign q[316] = ((!a[316])^(!b[316]))&(!c[316]); + assign q[317] = ((a[317])&(!b[317]))^(c[317]); + assign q[318] = (((!a[318])^(!b[318]))|(!c[318]))&(!d[318]); + assign q[319] = (((a[319])&(!b[319]))|(!c[319]))^(d[319]); + assign q[320] = (((!a[320])^(!b[320]))|(c[320]))|(d[320]); + assign q[321] = ((a[321])|(!b[321]))|((c[321])|(d[321])); + assign q[322] = ((!a[322])^(!b[322]))&(!c[322]); + assign q[323] = (((!a[323])&(b[323]))|(c[323]))|(d[323]); + assign q[324] = ((!a[324])&(!b[324]))^((c[324])|(d[324])); + assign q[325] = ((a[325])&(!b[325]))|(!c[325]); + assign q[326] = ((!a[326])^(!b[326]))|(c[326]); + assign q[327] = (((a[327])|(b[327]))&((c[327])|(!d[327])))^(!e[327]); + assign q[328] = ((!a[328])|(b[328]))&((c[328])&(d[328])); + assign q[329] = (((a[329])&(b[329]))^(c[329]))|(!d[329]); + assign q[330] = ((a[330])|(b[330]))|((c[330])^(!d[330])); + assign q[331] = (((!a[331])|(b[331]))&(c[331]))&(d[331]); + assign q[332] = (((!a[332])&(b[332]))|((!c[332])^(d[332])))&(e[332]); + assign q[333] = (((a[333])^(!b[333]))|(!c[333]))|(!d[333]); + assign q[334] = ((a[334])&(!b[334]))&((c[334])&(!d[334])); + assign q[335] = (((!a[335])&(b[335]))^((c[335])&(d[335])))&(e[335]); + assign q[336] = (!a[336])^(b[336]); + assign q[337] = (((a[337])^(b[337]))&(c[337]))&(d[337]); + assign q[338] = (((a[338])^(!b[338]))&(!c[338]))&(!d[338]); + assign q[339] = (((!a[339])^(!b[339]))|((!c[339])|(d[339])))&(!e[339]); + assign q[340] = (((a[340])^(b[340]))^((c[340])^(d[340])))|(e[340]); + assign q[341] = (((!a[341])&(!b[341]))^(!c[341]))^(d[341]); + assign q[342] = (a[342])^(!b[342]); + assign q[343] = (((!a[343])^(b[343]))&((c[343])|(!d[343])))|(e[343]); + assign q[344] = ((a[344])&(b[344]))&((!c[344])&(d[344])); + assign q[345] = (((!a[345])&(b[345]))&((c[345])^(!d[345])))|(e[345]); + assign q[346] = (((a[346])^(!b[346]))&(c[346]))&(!d[346]); + assign q[347] = (((!a[347])^(!b[347]))|((c[347])|(!d[347])))|(!e[347]); + assign q[348] = (((a[348])|(b[348]))&((!c[348])&(d[348])))|(!e[348]); + assign q[349] = (!a[349])&(b[349]); + assign q[350] = (((!a[350])^(b[350]))|((c[350])|(!d[350])))&(e[350]); + assign q[351] = (((!a[351])^(!b[351]))^((c[351])|(!d[351])))&(!e[351]); + assign q[352] = ((!a[352])|(b[352]))^(c[352]); + assign q[353] = (((a[353])&(!b[353]))^((c[353])&(!d[353])))|(!e[353]); + assign q[354] = (((!a[354])^(b[354]))^((!c[354])^(d[354])))^(e[354]); + assign q[355] = (a[355])|(b[355]); + assign q[356] = (((a[356])^(!b[356]))^(!c[356]))&(!d[356]); + assign q[357] = (((a[357])&(!b[357]))^((c[357])&(!d[357])))^(e[357]); + assign q[358] = (((a[358])&(b[358]))&((!c[358])&(d[358])))|(e[358]); + assign q[359] = (((a[359])&(!b[359]))&((!c[359])^(!d[359])))&(e[359]); + assign q[360] = (((!a[360])|(!b[360]))|((!c[360])|(d[360])))&(!e[360]); + assign q[361] = (((a[361])|(b[361]))^((c[361])|(!d[361])))^(!e[361]); + assign q[362] = (((!a[362])|(!b[362]))|(c[362]))|(d[362]); + assign q[363] = ((a[363])|(!b[363]))&((!c[363])^(!d[363])); + assign q[364] = (((a[364])&(!b[364]))&((c[364])&(d[364])))|(e[364]); + assign q[365] = ((a[365])|(!b[365]))&((c[365])|(d[365])); + assign q[366] = (((a[366])|(!b[366]))&((!c[366])|(!d[366])))^(!e[366]); + assign q[367] = ((a[367])^(!b[367]))^((!c[367])|(!d[367])); + assign q[368] = (((!a[368])&(b[368]))&((!c[368])|(!d[368])))&(!e[368]); + assign q[369] = ((a[369])^(b[369]))|((c[369])|(d[369])); + assign q[370] = (((a[370])^(b[370]))^(!c[370]))|(!d[370]); + assign q[371] = (((a[371])^(b[371]))&((!c[371])&(d[371])))&(!e[371]); + assign q[372] = (((!a[372])|(!b[372]))|((!c[372])&(d[372])))&(!e[372]); + assign q[373] = ((a[373])&(b[373]))&((!c[373])&(!d[373])); + assign q[374] = (((!a[374])^(!b[374]))^((!c[374])&(!d[374])))|(e[374]); + assign q[375] = ((!a[375])&(b[375]))^(!c[375]); + assign q[376] = (!a[376])|(b[376]); + assign q[377] = (((!a[377])^(b[377]))^((!c[377])^(!d[377])))^(e[377]); + assign q[378] = (((a[378])|(!b[378]))^((c[378])^(d[378])))&(!e[378]); + assign q[379] = (((a[379])|(b[379]))&((!c[379])^(!d[379])))^(e[379]); + assign q[380] = (((!a[380])^(!b[380]))^((c[380])|(!d[380])))&(!e[380]); + assign q[381] = (((!a[381])^(b[381]))^(c[381]))^(!d[381]); + assign q[382] = (((!a[382])^(b[382]))^(!c[382]))&(!d[382]); + assign q[383] = (((!a[383])&(!b[383]))&((c[383])^(d[383])))|(e[383]); + assign q[384] = (((a[384])&(b[384]))&((c[384])&(d[384])))^(e[384]); + assign q[385] = ((a[385])|(!b[385]))&(!c[385]); + assign q[386] = ((a[386])|(b[386]))&(!c[386]); + assign q[387] = (((!a[387])^(b[387]))|(c[387]))^(d[387]); + assign q[388] = (!a[388])&(!b[388]); + assign q[389] = ((a[389])^(b[389]))^(!c[389]); + assign q[390] = (((!a[390])|(b[390]))^(c[390]))|(d[390]); + assign q[391] = (!a[391])^(!b[391]); + assign q[392] = ((!a[392])^(b[392]))|(c[392]); + assign q[393] = (((!a[393])&(!b[393]))^((c[393])^(d[393])))^(e[393]); + assign q[394] = (((!a[394])^(b[394]))|(!c[394]))|(!d[394]); + assign q[395] = ((!a[395])&(!b[395]))^(!c[395]); + assign q[396] = ((a[396])^(b[396]))|((!c[396])|(d[396])); + assign q[397] = (((a[397])|(!b[397]))&((!c[397])&(d[397])))&(!e[397]); + assign q[398] = (((a[398])&(!b[398]))^((c[398])&(d[398])))|(!e[398]); + assign q[399] = (((!a[399])^(!b[399]))^((c[399])&(d[399])))|(!e[399]); + assign q[400] = (!a[400])&(b[400]); + assign q[401] = (((a[401])&(!b[401]))^(!c[401]))^(!d[401]); + assign q[402] = ((a[402])|(!b[402]))^((!c[402])^(!d[402])); + assign q[403] = (((!a[403])|(!b[403]))|((c[403])|(d[403])))^(!e[403]); + assign q[404] = (((!a[404])^(b[404]))&((c[404])&(d[404])))^(!e[404]); + assign q[405] = (((!a[405])^(b[405]))&(!c[405]))&(d[405]); + assign q[406] = (((!a[406])|(b[406]))|((c[406])^(!d[406])))&(!e[406]); + assign q[407] = (!a[407])|(b[407]); + assign q[408] = (((!a[408])&(!b[408]))|(!c[408]))&(!d[408]); + assign q[409] = (!a[409])^(!b[409]); + assign q[410] = (((!a[410])|(b[410]))|((!c[410])|(d[410])))|(!e[410]); + assign q[411] = (((!a[411])^(b[411]))|((c[411])&(d[411])))|(e[411]); + assign q[412] = ((!a[412])&(!b[412]))&((c[412])|(d[412])); + assign q[413] = (((!a[413])^(b[413]))^((c[413])&(!d[413])))&(!e[413]); + assign q[414] = (a[414])^(!b[414]); + assign q[415] = (((!a[415])^(b[415]))&(c[415]))|(!d[415]); + assign q[416] = ((!a[416])|(!b[416]))&(c[416]); + assign q[417] = (((!a[417])^(!b[417]))^((c[417])^(d[417])))|(!e[417]); + assign q[418] = ((!a[418])&(!b[418]))^((!c[418])&(!d[418])); + assign q[419] = (!a[419])&(!b[419]); + assign q[420] = ((a[420])^(!b[420]))|(!c[420]); + assign q[421] = ((!a[421])&(!b[421]))&((!c[421])^(d[421])); + assign q[422] = ((!a[422])^(b[422]))^((!c[422])&(!d[422])); + assign q[423] = (((a[423])|(b[423]))^((c[423])&(d[423])))^(!e[423]); + assign q[424] = (a[424])|(b[424]); + assign q[425] = (!a[425])^(b[425]); + assign q[426] = (!a[426])^(!b[426]); + assign q[427] = ((a[427])&(!b[427]))^((!c[427])^(!d[427])); + assign q[428] = (((!a[428])&(!b[428]))^(!c[428]))^(d[428]); + assign q[429] = (((!a[429])^(b[429]))&(c[429]))&(d[429]); + assign q[430] = ((!a[430])&(b[430]))^((c[430])^(d[430])); + assign q[431] = (((!a[431])|(b[431]))^((!c[431])|(d[431])))&(e[431]); + assign q[432] = (((!a[432])|(!b[432]))|((!c[432])^(d[432])))|(!e[432]); + assign q[433] = (((a[433])^(!b[433]))&(!c[433]))&(d[433]); + assign q[434] = ((!a[434])&(!b[434]))|((c[434])|(!d[434])); + assign q[435] = (!a[435])|(!b[435]); + assign q[436] = (((!a[436])^(!b[436]))&((!c[436])|(!d[436])))&(e[436]); + assign q[437] = (((!a[437])^(!b[437]))^(!c[437]))&(!d[437]); + assign q[438] = (!a[438])^(b[438]); + assign q[439] = ((a[439])^(!b[439]))|(c[439]); + assign q[440] = ((a[440])^(!b[440]))|((!c[440])&(!d[440])); + assign q[441] = (((a[441])&(b[441]))^((c[441])&(d[441])))|(e[441]); + assign q[442] = (!a[442])|(b[442]); + assign q[443] = (!a[443])^(b[443]); + assign q[444] = ((a[444])^(!b[444]))|((c[444])&(d[444])); + assign q[445] = (((a[445])|(!b[445]))&(c[445]))|(d[445]); + assign q[446] = ((a[446])&(b[446]))^((c[446])^(!d[446])); + assign q[447] = ((!a[447])&(!b[447]))^(!c[447]); + assign q[448] = (((!a[448])^(b[448]))&(c[448]))|(!d[448]); + assign q[449] = (((a[449])|(!b[449]))|((c[449])&(d[449])))|(!e[449]); + assign q[450] = (((!a[450])|(b[450]))|(c[450]))^(!d[450]); + assign q[451] = ((a[451])^(b[451]))|((c[451])^(d[451])); + assign q[452] = (((a[452])^(!b[452]))|(c[452]))|(d[452]); + assign q[453] = (!a[453])^(!b[453]); + assign q[454] = (((a[454])|(!b[454]))|(c[454]))^(d[454]); + assign q[455] = ((!a[455])&(b[455]))^((!c[455])|(!d[455])); + assign q[456] = (((a[456])^(!b[456]))|((c[456])^(d[456])))^(e[456]); + assign q[457] = (((a[457])&(!b[457]))^(c[457]))|(d[457]); + assign q[458] = (((!a[458])^(b[458]))^(c[458]))&(!d[458]); + assign q[459] = (((!a[459])&(b[459]))^((!c[459])&(!d[459])))&(e[459]); + assign q[460] = ((!a[460])^(!b[460]))|((c[460])&(!d[460])); + assign q[461] = ((a[461])&(!b[461]))&(c[461]); + assign q[462] = (((a[462])|(b[462]))^((c[462])^(d[462])))|(!e[462]); + assign q[463] = (((a[463])|(!b[463]))&((!c[463])|(d[463])))|(e[463]); + assign q[464] = (((!a[464])^(b[464]))&((!c[464])|(!d[464])))^(e[464]); + assign q[465] = ((!a[465])|(b[465]))&((c[465])&(d[465])); + assign q[466] = (((!a[466])^(b[466]))|((!c[466])&(d[466])))&(e[466]); + assign q[467] = (((!a[467])|(!b[467]))&((!c[467])&(!d[467])))^(!e[467]); + assign q[468] = ((!a[468])^(!b[468]))&((c[468])&(!d[468])); + assign q[469] = (((a[469])^(b[469]))^(c[469]))&(!d[469]); + assign q[470] = (((a[470])&(b[470]))^(c[470]))&(d[470]); + assign q[471] = (((!a[471])&(b[471]))^(c[471]))&(!d[471]); + assign q[472] = (((!a[472])|(b[472]))|((!c[472])|(!d[472])))|(!e[472]); + assign q[473] = (((a[473])|(b[473]))|((c[473])^(d[473])))|(e[473]); + assign q[474] = (a[474])^(!b[474]); + assign q[475] = (a[475])&(!b[475]); + assign q[476] = (((a[476])^(!b[476]))&((c[476])&(d[476])))|(!e[476]); + assign q[477] = ((a[477])^(b[477]))&(!c[477]); + assign q[478] = (((a[478])|(!b[478]))&((c[478])^(d[478])))|(e[478]); + assign q[479] = (((a[479])|(!b[479]))&((c[479])&(!d[479])))|(!e[479]); + assign q[480] = (((!a[480])|(b[480]))&((!c[480])|(!d[480])))|(e[480]); + assign q[481] = (((!a[481])&(b[481]))|((!c[481])^(d[481])))&(!e[481]); + assign q[482] = (a[482])^(!b[482]); + assign q[483] = (a[483])|(b[483]); + assign q[484] = (((a[484])|(!b[484]))&(c[484]))|(!d[484]); + assign q[485] = (((a[485])^(!b[485]))&(c[485]))|(!d[485]); + assign q[486] = ((!a[486])^(b[486]))&(c[486]); + assign q[487] = ((!a[487])&(b[487]))^((!c[487])^(!d[487])); + assign q[488] = ((a[488])&(b[488]))^((c[488])^(d[488])); + assign q[489] = (a[489])^(!b[489]); + assign q[490] = (((!a[490])^(b[490]))&(!c[490]))|(!d[490]); + assign q[491] = (a[491])^(!b[491]); + assign q[492] = (!a[492])|(!b[492]); + assign q[493] = (((!a[493])|(!b[493]))|((c[493])^(d[493])))^(e[493]); + assign q[494] = (((a[494])&(b[494]))^((c[494])&(!d[494])))&(e[494]); + assign q[495] = (((a[495])|(!b[495]))|((!c[495])^(d[495])))^(!e[495]); + assign q[496] = (((a[496])^(b[496]))&((!c[496])^(!d[496])))&(!e[496]); + assign q[497] = (((a[497])^(!b[497]))|((!c[497])&(d[497])))|(e[497]); + assign q[498] = (((!a[498])|(!b[498]))&(c[498]))&(!d[498]); + assign q[499] = (((a[499])|(b[499]))^((c[499])^(!d[499])))|(!e[499]); + assign q[500] = ((a[500])&(b[500]))^((!c[500])^(!d[500])); + assign q[501] = (((a[501])&(!b[501]))|(!c[501]))|(!d[501]); + assign q[502] = (((a[502])^(!b[502]))^(!c[502]))|(!d[502]); + assign q[503] = (((!a[503])&(b[503]))|(!c[503]))^(!d[503]); + assign q[504] = (((a[504])&(b[504]))|(c[504]))|(d[504]); + assign q[505] = (((!a[505])&(b[505]))&((c[505])&(!d[505])))^(!e[505]); + assign q[506] = (((!a[506])|(b[506]))|(c[506]))&(!d[506]); + assign q[507] = (((!a[507])^(b[507]))^(c[507]))&(d[507]); + assign q[508] = (((!a[508])&(b[508]))|((!c[508])|(d[508])))|(!e[508]); + assign q[509] = (!a[509])|(b[509]); + assign q[510] = (((a[510])|(!b[510]))^(c[510]))^(d[510]); + assign q[511] = ((!a[511])|(!b[511]))|((c[511])|(d[511])); + assign q[512] = ((a[512])|(!b[512]))&((!c[512])&(d[512])); + assign q[513] = (!a[513])&(!b[513]); + assign q[514] = (((a[514])|(!b[514]))^(!c[514]))&(d[514]); + assign q[515] = (((!a[515])|(!b[515]))^(c[515]))|(!d[515]); + assign q[516] = ((a[516])|(b[516]))|((c[516])&(!d[516])); + assign q[517] = (((a[517])^(!b[517]))^((c[517])&(!d[517])))&(e[517]); + assign q[518] = (((a[518])&(!b[518]))^((c[518])|(!d[518])))^(!e[518]); + assign q[519] = (((!a[519])^(!b[519]))&((c[519])&(d[519])))|(e[519]); + assign q[520] = ((a[520])^(b[520]))|((c[520])&(!d[520])); + assign q[521] = (((!a[521])^(!b[521]))^((!c[521])|(!d[521])))|(e[521]); + assign q[522] = (((a[522])|(b[522]))|(c[522]))&(!d[522]); + assign q[523] = (((a[523])|(b[523]))^((c[523])|(!d[523])))|(e[523]); + assign q[524] = ((!a[524])^(b[524]))&(c[524]); + assign q[525] = (a[525])&(b[525]); + assign q[526] = ((a[526])|(!b[526]))^((!c[526])^(!d[526])); + assign q[527] = ((!a[527])&(!b[527]))&((!c[527])|(!d[527])); + assign q[528] = ((a[528])^(b[528]))&(!c[528]); + assign q[529] = ((a[529])^(b[529]))|((c[529])|(!d[529])); + assign q[530] = ((!a[530])&(b[530]))|((c[530])&(!d[530])); + assign q[531] = (a[531])|(b[531]); + assign q[532] = (((!a[532])|(b[532]))&(c[532]))|(d[532]); + assign q[533] = ((a[533])&(!b[533]))&((c[533])&(!d[533])); + assign q[534] = (((a[534])&(!b[534]))&(!c[534]))|(d[534]); + assign q[535] = ((!a[535])^(b[535]))|((c[535])&(!d[535])); + assign q[536] = (((a[536])&(!b[536]))|((c[536])|(!d[536])))|(!e[536]); + assign q[537] = ((!a[537])^(b[537]))^((c[537])&(!d[537])); + assign q[538] = (((!a[538])&(b[538]))&(c[538]))|(d[538]); + assign q[539] = (a[539])|(!b[539]); + assign q[540] = (((!a[540])|(!b[540]))^((!c[540])|(d[540])))&(!e[540]); + assign q[541] = ((!a[541])&(!b[541]))&((c[541])^(d[541])); + assign q[542] = (((a[542])|(!b[542]))^(!c[542]))&(d[542]); + assign q[543] = (((!a[543])&(b[543]))&(!c[543]))^(d[543]); + assign q[544] = (((!a[544])|(b[544]))&((!c[544])&(d[544])))&(e[544]); + assign q[545] = (((a[545])^(!b[545]))|(!c[545]))&(d[545]); + assign q[546] = (((!a[546])|(!b[546]))^((!c[546])|(d[546])))&(!e[546]); + assign q[547] = (((a[547])^(!b[547]))|(c[547]))|(!d[547]); + assign q[548] = (((!a[548])^(b[548]))&((c[548])|(!d[548])))&(!e[548]); + assign q[549] = ((a[549])|(!b[549]))^((!c[549])^(d[549])); + assign q[550] = ((!a[550])&(!b[550]))^(c[550]); + assign q[551] = (((!a[551])&(b[551]))|((c[551])&(!d[551])))&(!e[551]); + assign q[552] = (((a[552])|(b[552]))^((!c[552])|(!d[552])))&(!e[552]); + assign q[553] = (((a[553])|(b[553]))|((!c[553])|(d[553])))&(e[553]); + assign q[554] = (((a[554])&(!b[554]))^(!c[554]))^(d[554]); + assign q[555] = ((!a[555])^(b[555]))|(!c[555]); + assign q[556] = (((!a[556])^(!b[556]))^(!c[556]))|(d[556]); + assign q[557] = ((a[557])&(!b[557]))|((c[557])&(d[557])); + assign q[558] = (((a[558])&(!b[558]))^((!c[558])^(!d[558])))^(!e[558]); + assign q[559] = (((!a[559])|(!b[559]))|((!c[559])|(!d[559])))^(!e[559]); + assign q[560] = ((!a[560])|(!b[560]))|((!c[560])^(d[560])); + assign q[561] = (((!a[561])|(b[561]))|(c[561]))^(!d[561]); + assign q[562] = ((!a[562])&(!b[562]))&((!c[562])^(!d[562])); + assign q[563] = (((a[563])^(b[563]))^(!c[563]))|(d[563]); + assign q[564] = (((!a[564])|(b[564]))|(!c[564]))|(d[564]); + assign q[565] = (((a[565])&(!b[565]))&((c[565])|(d[565])))|(!e[565]); + assign q[566] = (((a[566])&(b[566]))^(!c[566]))|(!d[566]); + assign q[567] = (((!a[567])&(!b[567]))&((c[567])&(!d[567])))|(!e[567]); + assign q[568] = (((!a[568])|(!b[568]))|(!c[568]))&(!d[568]); + assign q[569] = (!a[569])&(b[569]); + assign q[570] = ((!a[570])|(b[570]))|((!c[570])|(!d[570])); + assign q[571] = (((a[571])|(b[571]))&((!c[571])|(!d[571])))&(!e[571]); + assign q[572] = ((!a[572])&(b[572]))|((c[572])|(!d[572])); + assign q[573] = ((a[573])^(!b[573]))|((!c[573])|(!d[573])); + assign q[574] = (((!a[574])|(b[574]))&((!c[574])&(!d[574])))&(!e[574]); + assign q[575] = (((!a[575])&(b[575]))&((!c[575])|(!d[575])))|(!e[575]); + assign q[576] = (((a[576])|(!b[576]))^((c[576])^(d[576])))|(e[576]); + assign q[577] = (((a[577])|(b[577]))&(c[577]))^(d[577]); + assign q[578] = ((a[578])^(!b[578]))&(!c[578]); + assign q[579] = (((!a[579])&(!b[579]))^((!c[579])|(!d[579])))|(!e[579]); + assign q[580] = (((!a[580])|(b[580]))|(!c[580]))^(d[580]); + assign q[581] = ((a[581])&(b[581]))&((!c[581])|(d[581])); + assign q[582] = (((!a[582])^(b[582]))|(!c[582]))&(!d[582]); + assign q[583] = (a[583])&(b[583]); + assign q[584] = (((!a[584])|(b[584]))|((c[584])^(d[584])))^(e[584]); + assign q[585] = ((a[585])&(b[585]))|((!c[585])|(!d[585])); + assign q[586] = (((a[586])|(b[586]))^((!c[586])|(d[586])))^(!e[586]); + assign q[587] = ((a[587])|(!b[587]))&(!c[587]); + assign q[588] = (((!a[588])&(b[588]))^((!c[588])|(!d[588])))|(!e[588]); + assign q[589] = (((!a[589])|(b[589]))&((!c[589])|(d[589])))&(e[589]); + assign q[590] = (((a[590])^(b[590]))^((!c[590])&(d[590])))&(!e[590]); + assign q[591] = ((a[591])^(!b[591]))|(!c[591]); + assign q[592] = ((a[592])|(!b[592]))^((!c[592])^(!d[592])); + assign q[593] = ((!a[593])|(b[593]))&((c[593])&(!d[593])); + assign q[594] = ((!a[594])&(!b[594]))^(!c[594]); + assign q[595] = (((!a[595])^(b[595]))|(!c[595]))&(d[595]); + assign q[596] = (((a[596])|(!b[596]))&(!c[596]))|(!d[596]); + assign q[597] = (a[597])&(!b[597]); + assign q[598] = ((a[598])^(b[598]))|((!c[598])&(d[598])); + assign q[599] = ((a[599])&(b[599]))&(!c[599]); + assign q[600] = ((!a[600])|(b[600]))|((!c[600])^(!d[600])); + assign q[601] = ((a[601])&(!b[601]))|((c[601])^(!d[601])); + assign q[602] = ((!a[602])&(!b[602]))^(!c[602]); + assign q[603] = ((a[603])&(!b[603]))^((c[603])&(!d[603])); + assign q[604] = ((a[604])&(!b[604]))&((c[604])^(!d[604])); + assign q[605] = (((!a[605])|(b[605]))&((c[605])|(d[605])))|(e[605]); + assign q[606] = (((a[606])|(!b[606]))|((!c[606])^(d[606])))&(e[606]); + assign q[607] = ((a[607])&(!b[607]))^(!c[607]); + assign q[608] = ((a[608])^(!b[608]))&(!c[608]); + assign q[609] = ((a[609])^(!b[609]))&((!c[609])&(d[609])); + assign q[610] = ((!a[610])&(!b[610]))&(c[610]); + assign q[611] = (((!a[611])|(b[611]))&(!c[611]))|(!d[611]); + assign q[612] = (((a[612])^(!b[612]))&((!c[612])|(d[612])))^(e[612]); + assign q[613] = (((!a[613])|(b[613]))^((c[613])&(!d[613])))|(!e[613]); + assign q[614] = (a[614])^(!b[614]); + assign q[615] = (((!a[615])^(b[615]))|(!c[615]))^(d[615]); + assign q[616] = ((!a[616])&(!b[616]))|((c[616])&(!d[616])); + assign q[617] = (((a[617])^(b[617]))|(c[617]))|(d[617]); + assign q[618] = (((!a[618])^(!b[618]))^((c[618])^(d[618])))^(e[618]); + assign q[619] = (((!a[619])|(!b[619]))|(c[619]))^(!d[619]); + assign q[620] = (!a[620])^(!b[620]); + assign q[621] = ((!a[621])&(!b[621]))&((!c[621])|(d[621])); + assign q[622] = (((a[622])^(b[622]))&((!c[622])|(!d[622])))|(!e[622]); + assign q[623] = (((a[623])&(!b[623]))^(c[623]))&(d[623]); + assign q[624] = (((a[624])&(b[624]))|(c[624]))^(!d[624]); + assign q[625] = (((!a[625])^(!b[625]))&((!c[625])|(d[625])))&(!e[625]); + assign q[626] = (((!a[626])&(!b[626]))^(!c[626]))|(!d[626]); + assign q[627] = ((!a[627])|(b[627]))|(c[627]); + assign q[628] = (((!a[628])&(!b[628]))^((!c[628])|(!d[628])))^(e[628]); + assign q[629] = ((a[629])&(!b[629]))|((c[629])^(d[629])); + assign q[630] = (((!a[630])|(b[630]))^((c[630])|(!d[630])))|(!e[630]); + assign q[631] = (((!a[631])&(b[631]))&(!c[631]))^(!d[631]); + assign q[632] = (((a[632])&(!b[632]))&(!c[632]))|(!d[632]); + assign q[633] = (((a[633])|(b[633]))^(c[633]))|(d[633]); + assign q[634] = (((a[634])&(b[634]))|((c[634])|(d[634])))&(e[634]); + assign q[635] = (((a[635])&(!b[635]))&((c[635])&(!d[635])))&(!e[635]); + assign q[636] = (((!a[636])|(b[636]))^((!c[636])^(d[636])))^(e[636]); + assign q[637] = (((a[637])&(b[637]))&((!c[637])&(d[637])))|(!e[637]); + assign q[638] = ((!a[638])|(!b[638]))&(!c[638]); + assign q[639] = (((a[639])|(!b[639]))&((c[639])|(d[639])))^(!e[639]); + assign q[640] = (!a[640])&(b[640]); + assign q[641] = ((!a[641])^(!b[641]))^((!c[641])^(d[641])); + assign q[642] = (((!a[642])^(!b[642]))&(c[642]))&(!d[642]); + assign q[643] = (!a[643])^(!b[643]); + assign q[644] = ((!a[644])|(!b[644]))|((!c[644])^(!d[644])); + assign q[645] = (((a[645])&(b[645]))^((c[645])&(!d[645])))|(e[645]); + assign q[646] = ((a[646])&(!b[646]))&(c[646]); + assign q[647] = ((a[647])&(!b[647]))^((!c[647])^(!d[647])); + assign q[648] = (((!a[648])|(b[648]))&((!c[648])&(d[648])))|(e[648]); + assign q[649] = ((!a[649])|(b[649]))|((c[649])&(!d[649])); + assign q[650] = (((!a[650])&(b[650]))^(c[650]))^(d[650]); + assign q[651] = ((a[651])|(b[651]))^(!c[651]); + assign q[652] = (((a[652])^(b[652]))&((c[652])|(d[652])))|(e[652]); + assign q[653] = (((!a[653])|(b[653]))^(c[653]))^(d[653]); + assign q[654] = (((a[654])^(!b[654]))^(!c[654]))^(d[654]); + assign q[655] = (((!a[655])|(b[655]))|((!c[655])&(d[655])))&(e[655]); + assign q[656] = (((a[656])^(b[656]))|((!c[656])^(!d[656])))^(!e[656]); + assign q[657] = ((a[657])&(!b[657]))&(c[657]); + assign q[658] = (((!a[658])|(b[658]))|((!c[658])^(!d[658])))|(e[658]); + assign q[659] = (((a[659])&(b[659]))&((c[659])|(!d[659])))^(e[659]); + assign q[660] = (((!a[660])&(!b[660]))|((!c[660])|(!d[660])))&(e[660]); + assign q[661] = ((a[661])&(!b[661]))^((!c[661])^(!d[661])); + assign q[662] = (((!a[662])|(!b[662]))|((c[662])|(!d[662])))^(!e[662]); + assign q[663] = (a[663])^(b[663]); + assign q[664] = ((!a[664])&(!b[664]))|((!c[664])&(!d[664])); + assign q[665] = (((a[665])|(b[665]))&((!c[665])&(!d[665])))^(e[665]); + assign q[666] = (((a[666])&(b[666]))|((c[666])|(d[666])))|(!e[666]); + assign q[667] = ((a[667])&(!b[667]))&(!c[667]); + assign q[668] = (((!a[668])&(b[668]))^(c[668]))|(d[668]); + assign q[669] = (((!a[669])^(b[669]))^((!c[669])^(!d[669])))&(e[669]); + assign q[670] = ((!a[670])|(b[670]))&((c[670])|(!d[670])); + assign q[671] = (((a[671])&(!b[671]))^(c[671]))^(!d[671]); + assign q[672] = (((a[672])^(b[672]))^((c[672])&(!d[672])))|(!e[672]); + assign q[673] = (((a[673])^(!b[673]))&((c[673])|(d[673])))|(!e[673]); + assign q[674] = ((!a[674])|(!b[674]))|((!c[674])^(d[674])); + assign q[675] = ((!a[675])^(b[675]))&((!c[675])|(!d[675])); + assign q[676] = (((!a[676])&(!b[676]))^((!c[676])&(d[676])))|(!e[676]); + assign q[677] = (((!a[677])&(!b[677]))^(c[677]))&(!d[677]); + assign q[678] = (((!a[678])|(!b[678]))&((c[678])^(!d[678])))&(e[678]); + assign q[679] = (!a[679])|(b[679]); + assign q[680] = (a[680])&(!b[680]); + assign q[681] = (((!a[681])|(b[681]))|((c[681])&(d[681])))|(!e[681]); + assign q[682] = ((a[682])&(!b[682]))^((!c[682])|(!d[682])); + assign q[683] = ((!a[683])&(!b[683]))|((!c[683])^(!d[683])); + assign q[684] = (!a[684])&(!b[684]); + assign q[685] = (((a[685])&(!b[685]))|((c[685])&(!d[685])))|(!e[685]); + assign q[686] = (a[686])^(!b[686]); + assign q[687] = (((!a[687])|(b[687]))&((!c[687])|(d[687])))^(e[687]); + assign q[688] = (((a[688])^(!b[688]))^((!c[688])&(!d[688])))&(!e[688]); + assign q[689] = (((!a[689])^(b[689]))^(c[689]))&(!d[689]); + assign q[690] = ((a[690])^(!b[690]))|((!c[690])|(!d[690])); + assign q[691] = (((!a[691])^(b[691]))&((!c[691])&(!d[691])))^(!e[691]); + assign q[692] = ((a[692])^(!b[692]))|((c[692])|(!d[692])); + assign q[693] = (((a[693])&(b[693]))&((!c[693])|(d[693])))^(e[693]); + assign q[694] = ((!a[694])^(!b[694]))^((c[694])^(!d[694])); + assign q[695] = ((a[695])&(b[695]))&((!c[695])|(!d[695])); + assign q[696] = ((a[696])|(b[696]))&((c[696])|(d[696])); + assign q[697] = (((!a[697])^(b[697]))|((c[697])^(!d[697])))|(e[697]); + assign q[698] = (((a[698])|(b[698]))|((!c[698])&(!d[698])))^(!e[698]); + assign q[699] = (((!a[699])|(!b[699]))&(c[699]))|(!d[699]); + assign q[700] = ((!a[700])&(b[700]))|((!c[700])|(d[700])); + assign q[701] = (((!a[701])|(b[701]))|((c[701])^(d[701])))&(e[701]); + assign q[702] = (((!a[702])&(b[702]))&(c[702]))&(!d[702]); + assign q[703] = ((!a[703])&(!b[703]))|((c[703])|(d[703])); + assign q[704] = (((a[704])^(b[704]))^((c[704])&(d[704])))|(!e[704]); + assign q[705] = ((a[705])^(!b[705]))^((!c[705])^(!d[705])); + assign q[706] = (((!a[706])^(b[706]))^((!c[706])&(!d[706])))&(e[706]); + assign q[707] = ((a[707])&(!b[707]))|((c[707])^(d[707])); + assign q[708] = (((a[708])^(b[708]))|(!c[708]))|(d[708]); + assign q[709] = (((!a[709])&(b[709]))^((!c[709])&(!d[709])))&(!e[709]); + assign q[710] = ((!a[710])^(!b[710]))^(c[710]); + assign q[711] = (!a[711])&(b[711]); + assign q[712] = ((a[712])^(b[712]))&((c[712])^(!d[712])); + assign q[713] = (((a[713])^(b[713]))|((!c[713])^(d[713])))|(!e[713]); + assign q[714] = (((a[714])|(b[714]))&((c[714])^(d[714])))^(!e[714]); + assign q[715] = ((a[715])|(b[715]))|((!c[715])^(d[715])); + assign q[716] = (((a[716])|(b[716]))^(c[716]))^(!d[716]); + assign q[717] = (((!a[717])|(!b[717]))^((!c[717])^(!d[717])))^(e[717]); + assign q[718] = (((a[718])^(!b[718]))&(!c[718]))^(d[718]); + assign q[719] = (((!a[719])^(!b[719]))&((!c[719])|(d[719])))&(!e[719]); + assign q[720] = ((!a[720])^(b[720]))|(c[720]); + assign q[721] = ((!a[721])&(!b[721]))|((c[721])&(d[721])); + assign q[722] = (((!a[722])|(!b[722]))&((!c[722])&(!d[722])))&(!e[722]); + assign q[723] = ((a[723])|(b[723]))&((c[723])|(d[723])); + assign q[724] = (((!a[724])|(b[724]))^((c[724])^(!d[724])))^(e[724]); + assign q[725] = (((!a[725])^(!b[725]))|(!c[725]))&(!d[725]); + assign q[726] = ((a[726])^(!b[726]))^(c[726]); + assign q[727] = ((!a[727])&(!b[727]))^(c[727]); + assign q[728] = ((!a[728])^(!b[728]))^(c[728]); + assign q[729] = ((!a[729])&(b[729]))&(!c[729]); + assign q[730] = (((a[730])|(!b[730]))&(!c[730]))&(!d[730]); + assign q[731] = ((!a[731])|(!b[731]))^(c[731]); + assign q[732] = ((!a[732])&(!b[732]))|(c[732]); + assign q[733] = ((!a[733])&(!b[733]))^(!c[733]); + assign q[734] = (((a[734])|(b[734]))|((!c[734])^(!d[734])))|(!e[734]); + assign q[735] = (a[735])^(b[735]); + assign q[736] = ((!a[736])|(b[736]))&(!c[736]); + assign q[737] = (((!a[737])|(b[737]))|(c[737]))&(!d[737]); + assign q[738] = (((a[738])^(b[738]))^((c[738])^(!d[738])))|(e[738]); + assign q[739] = (!a[739])^(b[739]); + assign q[740] = (((a[740])|(b[740]))^((!c[740])^(!d[740])))&(!e[740]); + assign q[741] = ((!a[741])&(b[741]))^((!c[741])^(!d[741])); + assign q[742] = (((a[742])|(b[742]))|(c[742]))|(d[742]); + assign q[743] = (((a[743])&(b[743]))&((c[743])&(d[743])))|(!e[743]); + assign q[744] = (((!a[744])&(!b[744]))^((!c[744])&(d[744])))^(e[744]); + assign q[745] = (((!a[745])&(b[745]))^((!c[745])|(!d[745])))^(e[745]); + assign q[746] = (((a[746])&(b[746]))^(!c[746]))&(!d[746]); + assign q[747] = (((!a[747])^(b[747]))^((!c[747])^(d[747])))|(e[747]); + assign q[748] = (((a[748])^(b[748]))|((c[748])^(d[748])))&(!e[748]); + assign q[749] = (((a[749])^(b[749]))|((c[749])|(!d[749])))|(e[749]); +endmodule diff --git a/tests/arch/gatemate/luttrees.ys b/tests/arch/gatemate/luttrees.ys new file mode 100644 index 000000000..545643226 --- /dev/null +++ b/tests/arch/gatemate/luttrees.ys @@ -0,0 +1,13 @@ + +read_verilog luttrees.v +design -save read + +hierarchy -top luttrees +proc +equiv_opt -async2sync -assert -map +/gatemate/cells_sim.v synth_gatemate -noiopad -luttree -nomx4 -nomx8 # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd luttrees # Constrain all select calls below inside the top module + +select -assert-count 750 t:CC_LUT2 t:CC_L2T4 t:CC_L2T5 %% +select -assert-none t:CC_LUT2 t:CC_L2T4 t:CC_L2T5 %% t:* %D + diff --git a/tests/arch/ice40/.gitignore b/tests/arch/ice40/.gitignore index 9a71dca69..54f908bdb 100644 --- a/tests/arch/ice40/.gitignore +++ b/tests/arch/ice40/.gitignore @@ -1,4 +1,5 @@ *.log +*.json /run-test.mk +*_synth.v +*_testbench diff --git a/tests/gen-tests-makefile.sh b/tests/gen-tests-makefile.sh index ab8fb7013..cde9ab1b9 100755 --- a/tests/gen-tests-makefile.sh +++ b/tests/gen-tests-makefile.sh @@ -17,7 +17,7 @@ generate_target() { generate_ys_test() { ys_file=$1 yosys_args=${2:-} - generate_target "$ys_file" "$YOSYS_BASEDIR/yosys -ql ${ys_file%.*}.log $yosys_args $ys_file" + generate_target "$ys_file" "\"$YOSYS_BASEDIR/yosys\" -ql ${ys_file%.*}.log $yosys_args $ys_file" } # $ generate_bash_test bash_file diff --git a/tests/tools/autotest.sh b/tests/tools/autotest.sh index e4aef9917..f96eb8d71 100755 --- a/tests/tools/autotest.sh +++ b/tests/tools/autotest.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -libs="" +libs=() genvcd=false use_xsim=false use_modelsim=false @@ -15,7 +15,7 @@ xinclude_opts="" minclude_opts="" scriptfiles="" scriptopt="" -toolsdir="$(cd $(dirname $0); pwd)" +toolsdir="$(cd "$(dirname "$0")"; pwd)" warn_iverilog_git=false # The following are used in verilog to firrtl regression tests. # Typically these will be passed as environment variables: @@ -25,8 +25,8 @@ firrtl2verilog="" xfirrtl="../xfirrtl" abcprog="$toolsdir/../../yosys-abc" -if [ ! -f $toolsdir/cmp_tbdata -o $toolsdir/cmp_tbdata.c -nt $toolsdir/cmp_tbdata ]; then - ( set -ex; ${CC:-gcc} -Wall -o $toolsdir/cmp_tbdata $toolsdir/cmp_tbdata.c; ) || exit 1 +if [ ! -f "$toolsdir/cmp_tbdata" -o "$toolsdir/cmp_tbdata.c" -nt "$toolsdir/cmp_tbdata" ]; then + ( set -ex; ${CC:-gcc} -Wall -o "$toolsdir/cmp_tbdata" "$toolsdir/cmp_tbdata.c"; ) || exit 1 fi while getopts xmGl:wkjvref:s:p:n:S:I:A:-: opt; do @@ -38,7 +38,7 @@ while getopts xmGl:wkjvref:s:p:n:S:I:A:-: opt; do G) warn_iverilog_git=true ;; l) - libs="$libs $(cd $(dirname $OPTARG); pwd)/$(basename $OPTARG)";; + libs+=("$(cd "$(dirname "$OPTARG")"; pwd)/$(basename "$OPTARG")");; w) genvcd=true ;; k) @@ -162,7 +162,7 @@ do cp ../${bn}_tb.v ${bn}_tb.v fi if $genvcd; then sed -i 's,// \$dump,$dump,g' ${bn}_tb.v; fi - compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.${refext} $libs \ + compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.${refext} "${libs[@]}" \ "$toolsdir"/../../techlibs/common/simlib.v \ "$toolsdir"/../../techlibs/common/simcells.v if $genvcd; then mv testbench.vcd ${bn}_ref.vcd; fi @@ -171,11 +171,11 @@ do test_passes() { "$toolsdir"/../../yosys -b "verilog $backend_opts" -o ${bn}_syn${test_count}.v "$@" compile_and_run ${bn}_tb_syn${test_count} ${bn}_out_syn${test_count} \ - ${bn}_tb.v ${bn}_syn${test_count}.v $libs \ + ${bn}_tb.v ${bn}_syn${test_count}.v "${libs[@]}" \ "$toolsdir"/../../techlibs/common/simlib.v \ "$toolsdir"/../../techlibs/common/simcells.v if $genvcd; then mv testbench.vcd ${bn}_syn${test_count}.vcd; fi - $toolsdir/cmp_tbdata ${bn}_out_ref ${bn}_out_syn${test_count} + "$toolsdir/cmp_tbdata" ${bn}_out_ref ${bn}_out_syn${test_count} test_count=$(( test_count + 1 )) } diff --git a/tests/various/aiger_dff.ys b/tests/various/aiger_dff.ys new file mode 100644 index 000000000..057f3d774 --- /dev/null +++ b/tests/various/aiger_dff.ys @@ -0,0 +1,7 @@ +read_verilog -icells <<EOT +module top(input clk, d, output q); +\$_DFF_N_ dffn(.C(clk), .D(d), .Q(q)); +endmodule +EOT +write_aiger -zinit -ywmap aiger_dff.out /dev/null +!grep -qF negedge aiger_dff.out diff --git a/tests/various/rename_scramble_name.ys b/tests/various/rename_scramble_name.ys new file mode 100644 index 000000000..9a36d0922 --- /dev/null +++ b/tests/various/rename_scramble_name.ys @@ -0,0 +1,31 @@ +read_verilog <<EOF +module top(); + wire a, b, c; +endmodule +EOF + +proc +hierarchy -top top +rename -seed 2 -scramble-name w:* +select -assert-none w:a w:b w:c +select -assert-count 3 w:$_*_ +select -assert-none w:$_*_ %% %n +design -reset + +read_verilog <<EOF +module foo(input a, b, output c); + assign c = a + b; +endmodule + +module top(); + wire a, b, c; + foo bar(.a(a), .b(b), .c(c)); +endmodule +EOF + +proc +hierarchy -top top +rename -seed 2 -scramble-name c:bar +select -assert-none c:bar +select -assert-count 1 c:$_*_ +select -assert-none c:$_*_ w:* foo/c:$add$<<EOF:2$1 %% %n diff --git a/tests/various/smtlib2_module-expected.smt2 b/tests/various/smtlib2_module-expected.smt2 index ace858ca8..74e2f3fca 100644 --- a/tests/various/smtlib2_module-expected.smt2 +++ b/tests/various/smtlib2_module-expected.smt2 @@ -4,11 +4,14 @@ (declare-fun |smtlib2_is| (|smtlib2_s|) Bool) (declare-fun |smtlib2#0| (|smtlib2_s|) (_ BitVec 8)) ; \a ; yosys-smt2-input a 8 +; yosys-smt2-witness {"offset": 0, "path": ["\\a"], "smtname": "a", "type": "input", "width": 8} (define-fun |smtlib2_n a| ((state |smtlib2_s|)) (_ BitVec 8) (|smtlib2#0| state)) (declare-fun |smtlib2#1| (|smtlib2_s|) (_ BitVec 8)) ; \b ; yosys-smt2-input b 8 +; yosys-smt2-witness {"offset": 0, "path": ["\\b"], "smtname": "b", "type": "input", "width": 8} (define-fun |smtlib2_n b| ((state |smtlib2_s|)) (_ BitVec 8) (|smtlib2#1| state)) ; yosys-smt2-output add 8 +; yosys-smt2-witness {"offset": 0, "path": ["\\add"], "smtname": "add", "type": "blackbox", "width": 8} (define-fun |smtlib2_n add| ((state |smtlib2_s|)) (_ BitVec 8) (let ( (|a| (|smtlib2_n a| state)) (|b| (|smtlib2_n b| state)) @@ -16,6 +19,7 @@ (bvadd a b) )) ; yosys-smt2-output eq 1 +; yosys-smt2-witness {"offset": 0, "path": ["\\eq"], "smtname": "eq", "type": "blackbox", "width": 1} (define-fun |smtlib2_n eq| ((state |smtlib2_s|)) Bool (let ( (|a| (|smtlib2_n a| state)) (|b| (|smtlib2_n b| state)) @@ -23,6 +27,7 @@ (= a b) )) ; yosys-smt2-output sub 8 +; yosys-smt2-witness {"offset": 0, "path": ["\\sub"], "smtname": "sub", "type": "blackbox", "width": 8} (define-fun |smtlib2_n sub| ((state |smtlib2_s|)) (_ BitVec 8) (let ( (|a| (|smtlib2_n a| state)) (|b| (|smtlib2_n b| state)) @@ -38,13 +43,16 @@ (declare-sort |uut_s| 0) (declare-fun |uut_is| (|uut_s|) Bool) ; yosys-smt2-cell smtlib2 s +; yosys-smt2-witness {"path": ["\\s"], "smtname": "s", "type": "cell"} (declare-fun |uut#0| (|uut_s|) (_ BitVec 8)) ; \add (declare-fun |uut#1| (|uut_s|) Bool) ; \eq (declare-fun |uut#2| (|uut_s|) (_ BitVec 8)) ; \sub (declare-fun |uut_h s| (|uut_s|) |smtlib2_s|) ; yosys-smt2-anyconst uut#3 8 smtlib2_module.v:14.17-14.26 +; yosys-smt2-witness {"offset": 0, "path": ["\\a"], "smtname": 3, "type": "init", "width": 8} (declare-fun |uut#3| (|uut_s|) (_ BitVec 8)) ; \a ; yosys-smt2-anyconst uut#4 8 smtlib2_module.v:14.32-14.41 +; yosys-smt2-witness {"offset": 0, "path": ["\\b"], "smtname": 4, "type": "init", "width": 8} (declare-fun |uut#4| (|uut_s|) (_ BitVec 8)) ; \b (define-fun |uut#5| ((state |uut_s|)) (_ BitVec 8) (bvadd (|uut#3| state) (|uut#4| state))) ; \add2 (define-fun |uut#6| ((state |uut_s|)) Bool (= (|uut#0| state) (|uut#5| state))) ; $0$formal$smtlib2_module.v:28$1_CHECK[0:0]$9 |