diff options
140 files changed, 4709 insertions, 1863 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..28d13da25 --- /dev/null +++ b/.clang-format @@ -0,0 +1,13 @@ +# Default Linux style +BasedOnStyle: LLVM +IndentWidth: 8 +UseTab: Always +BreakBeforeBraces: Linux +AllowShortIfStatementsOnASingleLine: false +IndentCaseLabels: false + +# From CodingReadme +TabWidth: 8 +ContinuationIndentWidth: 2 +ColumnLimit: 150 +# BreakBeforeBraces: Linux diff --git a/.gitignore b/.gitignore index e24f7975a..76f53cd06 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.gch *.gcda *.gcno +__pycache__ /.cproject /.project /.settings @@ -28,6 +29,8 @@ /yosys-smtbmc-script.py /yosys-filterlib /yosys-filterlib.exe +/kernel/*.pyh +/kernel/python_wrappers.cc /kernel/version_*.cc /share /yosys-win32-mxebin-* @@ -16,6 +16,7 @@ Yosys 0.8 .. Yosys 0.8-dev - Added "gate2lut.v" techmap rule - Added "rename -src" - Added "equiv_opt" pass + - Added "read_aiger" frontend - "synth_xilinx" to now infer hard shift registers, using new "shregmap -tech xilinx" @@ -2,6 +2,7 @@ CONFIG := clang # CONFIG := gcc # CONFIG := gcc-4.8 +# CONFIG := afl-gcc # CONFIG := emcc # CONFIG := mxe # CONFIG := msys2 @@ -21,11 +22,6 @@ ENABLE_PROTOBUF := 0 # python wrappers ENABLE_PYOSYS := 0 -PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)" -PYTHON_EXECUTABLE := $(shell if python3 -c ""; then echo "python3"; else echo "python"; fi) -PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"") -PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.) -PYTHON_DESTDIR := `$(PYTHON_EXECUTABLE)-config --prefix`/lib/python$(PYTHON_VERSION)/dist-packages # other configuration flags ENABLE_GCOV := 0 @@ -50,6 +46,10 @@ OS := $(shell uname -s) PREFIX ?= /usr/local INSTALL_SUDO := +ifneq ($(wildcard Makefile.conf),) +include Makefile.conf +endif + BINDIR := $(PREFIX)/bin LIBDIR := $(PREFIX)/lib DATDIR := $(PREFIX)/share/yosys @@ -89,6 +89,9 @@ PLUGIN_LDFLAGS += -undefined dynamic_lookup # homebrew search paths ifneq ($(shell which brew),) BREW_PREFIX := $(shell brew --prefix)/opt +$(info $$BREW_PREFIX is [${BREW_PREFIX}]) +CXXFLAGS += -I$(BREW_PREFIX)/boost/include/boost +LDFLAGS += -L$(BREW_PREFIX)/boost/lib CXXFLAGS += -I$(BREW_PREFIX)/readline/include LDFLAGS += -L$(BREW_PREFIX)/readline/lib PKG_CONFIG_PATH := $(BREW_PREFIX)/libffi/lib/pkgconfig:$(PKG_CONFIG_PATH) @@ -138,6 +141,21 @@ $(info $(subst $$--$$,$(newline),$(shell sed 's,^,[Makefile.conf] ,; s,$$,$$--$$ include Makefile.conf endif +ifeq ($(ENABLE_PYOSYS),1) +PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)" +PYTHON_EXECUTABLE := $(shell if python3 -c ""; then echo "python3"; else echo "python"; fi) +PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"") +PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.) +PYTHON_PREFIX := $(shell $(PYTHON_EXECUTABLE)-config --prefix) +PYTHON_DESTDIR := $(PYTHON_PREFIX)/lib/python$(PYTHON_VERSION)/site-packages + +# Reload Makefile.conf to override python specific variables if defined +ifneq ($(wildcard Makefile.conf),) +include Makefile.conf +endif + +endif + ifeq ($(CONFIG),clang) CXX = clang LD = clang++ @@ -185,6 +203,12 @@ LD = gcc-4.8 CXXFLAGS += -std=c++11 -Os ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" +else ifeq ($(CONFIG),afl-gcc) +CXX = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc +LD = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc +CXXFLAGS += -std=c++11 -Os +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" + else ifeq ($(CONFIG),cygwin) CXX = gcc LD = gcc @@ -272,29 +296,50 @@ endif ifeq ($(ENABLE_PYOSYS),1) #Detect name of boost_python library. Some distros usbe boost_python-py<version>, other boost_python<version>, some only use the major version number, some a concatenation of major and minor version numbers +ifeq ($(OS), Darwin) +BOOST_PYTHON_LIB ?= $(shell \ + if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))"; else \ + if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \ + if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))"; else \ + if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \ + echo ""; fi; fi; fi; fi;) +else BOOST_PYTHON_LIB ?= $(shell \ if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))"; else \ if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \ if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))"; else \ if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \ echo ""; fi; fi; fi; fi;) +endif ifeq ($(BOOST_PYTHON_LIB),) $(error BOOST_PYTHON_LIB could not be detected. Please define manualy) endif +ifeq ($(OS), Darwin) ifeq ($(PYTHON_MAJOR_VERSION),3) -LDLIBS += `$(PYTHON_EXECUTABLE)-config --libs` $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem -CXXFLAGS += `$(PYTHON_EXECUTABLE)-config --includes` -D WITH_PYTHON +LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem +CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON else -LDLIBS += `$(PYTHON_EXECUTABLE)-config --libs` $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem -CXXFLAGS += `$(PYTHON_EXECUTABLE)-config --includes` -D WITH_PYTHON +LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem +CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON +endif +else +ifeq ($(PYTHON_MAJOR_VERSION),3) +LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem +CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON +else +LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem +CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON +endif endif +ifeq ($(ENABLE_PYOSYS),1) PY_WRAPPER_FILE = kernel/python_wrappers OBJS += $(PY_WRAPPER_FILE).o PY_GEN_SCRIPT= py_wrap_generator -PY_WRAP_INCLUDES := $(shell python$(PYTHON_VERSION) -c "import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).print_includes()") +PY_WRAP_INCLUDES := $(shell python$(PYTHON_VERSION) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).print_includes()") +endif endif ifeq ($(ENABLE_READLINE),1) @@ -540,7 +585,11 @@ yosys$(EXE): $(OBJS) $(P) $(LD) -o yosys$(EXE) $(LDFLAGS) $(OBJS) $(LDLIBS) libyosys.so: $(filter-out kernel/driver.o,$(OBJS)) +ifeq ($(OS), Darwin) + $(P) $(LD) -o libyosys.so -shared -Wl,-install_name,libyosys.so $(LDFLAGS) $^ $(LDLIBS) +else $(P) $(LD) -o libyosys.so -shared -Wl,-soname,libyosys.so $(LDFLAGS) $^ $(LDLIBS) +endif %.o: %.cc $(Q) mkdir -p $(dir $@) @@ -550,9 +599,11 @@ libyosys.so: $(filter-out kernel/driver.o,$(OBJS)) $(Q) mkdir -p $(dir $@) $(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(LD) -x c++ -o $@ -E -P - -$(PY_WRAPPER_FILE).cc: $(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES) +ifeq ($(ENABLE_PYOSYS),1) +$(PY_WRAPPER_FILE).cc: misc/$(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES) $(Q) mkdir -p $(dir $@) - $(P) python$(PYTHON_VERSION) -c "import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).gen_wrappers(\"$(PY_WRAPPER_FILE).cc\")" + $(P) python$(PYTHON_VERSION) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).gen_wrappers(\"$(PY_WRAPPER_FILE).cc\")" +endif %.o: %.cpp $(Q) mkdir -p $(dir $@) @@ -679,13 +730,13 @@ endif $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(DATDIR) $(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(DATDIR)/. ifeq ($(ENABLE_LIBYOSYS),1) - $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR) + $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(LIBDIR) + $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)/ $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so - $(INSTALL_SUDO) ldconfig ifeq ($(ENABLE_PYOSYS),1) $(INSTALL_SUDO) mkdir -p $(PYTHON_DESTDIR)/pyosys - $(INSTALL_SUDO) cp libyosys.so $(PYTHON_DESTDIR)/pyosys - $(INSTALL_SUDO) cp __init__.py $(PYTHON_DESTDIR)/pyosys + $(INSTALL_SUDO) cp libyosys.so $(PYTHON_DESTDIR)/pyosys/ + $(INSTALL_SUDO) cp misc/__init__.py $(PYTHON_DESTDIR)/pyosys/ endif endif @@ -787,6 +838,9 @@ config-gcc-static: clean config-gcc-4.8: clean echo 'CONFIG := gcc-4.8' > Makefile.conf +config-afl-gcc: clean + echo 'CONFIG := afl-gcc' > Makefile.conf + config-emcc: clean echo 'CONFIG := emcc' > Makefile.conf echo 'ENABLE_TCL := 0' >> Makefile.conf @@ -833,5 +887,5 @@ echo-git-rev: -include techlibs/*/*.d .PHONY: all top-all abc test install install-abc manual clean mrproper qtcreator coverage vcxsrc mxebin -.PHONY: config-clean config-clang config-gcc config-gcc-static config-gcc-4.8 config-gprof config-sudo +.PHONY: config-clean config-clang config-gcc config-gcc-static config-gcc-4.8 config-afl-gcc config-gprof config-sudo @@ -257,13 +257,9 @@ for them: - Non-synthesizable language features as defined in IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002 -- The ``tri``, ``triand``, ``trior``, ``wand`` and ``wor`` net types +- The ``tri``, ``triand`` and ``trior`` net types -- The ``config`` keyword and library map files - -- The ``disable``, ``primitive`` and ``specify`` statements - -- Latched logic (is synthesized as logic with feedback loops) +- The ``config`` and ``disable`` keywords and library map files Verilog Attributes and non-standard features @@ -370,7 +366,7 @@ Verilog Attributes and non-standard features - When defining a macro with `define, all text between triple double quotes is interpreted as macro body, even if it contains unescaped newlines. The - tipple double quotes are removed from the macro body. For example: + triple double quotes are removed from the macro body. For example: `define MY_MACRO(a, b) """ assign a = 23; @@ -417,12 +413,18 @@ Verilog Attributes and non-standard features $ yosys -p 'plugin -a foo -i /lib/libm.so; read_verilog dpitest.v' - Sized constants (the syntax ``<size>'s?[bodh]<value>``) support constant - expressions as <size>. If the expression is not a simple identifier, it + expressions as ``<size>``. If the expression is not a simple identifier, it must be put in parentheses. Examples: ``WIDTH'd42``, ``(4+2)'b101010`` -- The system tasks ``$finish`` and ``$display`` are supported in initial blocks - in an unconditional context (only if/case statements on parameters - and constant values). The intended use for this is synthesis-time DRC. +- The system tasks ``$finish``, ``$stop`` and ``$display`` are supported in + initial blocks in an unconditional context (only if/case statements on + expressions over parameters and constant values are allowed). The intended + use for this is synthesis-time DRC. + +- There is limited support for converting specify .. endspecify statements to + special ``$specify2``, ``$specify3``, and ``$specrule`` cells, for use in + blackboxes and whiteboxes. Use ``read_verilog -specify`` to enable this + functionality. (By default specify .. endspecify blocks are ignored.) Non-standard or SystemVerilog features for formal verification @@ -457,7 +459,7 @@ Non-standard or SystemVerilog features for formal verification supported in any clocked block. - The syntax ``@($global_clock)`` can be used to create FFs that have no - explicit clock input ($ff cells). The same can be achieved by using + explicit clock input (``$ff`` cells). The same can be achieved by using ``@(posedge <netname>)`` or ``@(negedge <netname>)`` when ``<netname>`` is marked with the ``(* gclk *)`` Verilog attribute. @@ -470,7 +472,7 @@ from SystemVerilog: - The ``assert`` statement from SystemVerilog is supported in its most basic form. In module context: ``assert property (<expression>);`` and within an - always block: ``assert(<expression>);``. It is transformed to a $assert cell. + always block: ``assert(<expression>);``. It is transformed to an ``$assert`` cell. - The ``assume``, ``restrict``, and ``cover`` statements from SystemVerilog are also supported. The same limitations as with the ``assert`` statement apply. diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc index b6dbd84cb..a1761b662 100644 --- a/backends/blif/blif.cc +++ b/backends/blif/blif.cc @@ -409,12 +409,26 @@ struct BlifDumper f << stringf(".%s %s", subckt_or_gate(cell->type.str()), cstr(cell->type)); for (auto &conn : cell->connections()) - for (int i = 0; i < conn.second.size(); i++) { - if (conn.second.size() == 1) - f << stringf(" %s", cstr(conn.first)); - else - f << stringf(" %s[%d]", cstr(conn.first), i); - f << stringf("=%s", cstr(conn.second.extract(i, 1))); + { + if (conn.second.size() == 1) { + f << stringf(" %s=%s", cstr(conn.first), cstr(conn.second[0])); + continue; + } + + Module *m = design->module(cell->type); + Wire *w = m ? m->wire(conn.first) : nullptr; + + if (w == nullptr) { + for (int i = 0; i < GetSize(conn.second); i++) + f << stringf(" %s[%d]=%s", cstr(conn.first), i, cstr(conn.second[i])); + } else { + for (int i = 0; i < std::min(GetSize(conn.second), GetSize(w)); i++) { + SigBit sig(w, i); + f << stringf(" %s[%d]=%s", cstr(conn.first), sig.wire->upto ? + sig.wire->start_offset+sig.wire->width-sig.offset-1 : + sig.wire->start_offset+sig.offset, cstr(conn.second[i])); + } + } } f << stringf("\n"); diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index 91f238fa5..511a11942 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -129,7 +129,13 @@ struct BtorWorker void export_cell(Cell *cell) { - log_assert(cell_recursion_guard.count(cell) == 0); + if (cell_recursion_guard.count(cell)) { + string cell_list; + for (auto c : cell_recursion_guard) + cell_list += stringf("\n %s", log_id(c)); + log_error("Found topological loop while processing cell %s. Active cells:%s\n", log_id(cell), cell_list.c_str()); + } + cell_recursion_guard.insert(cell); btorf_push(log_id(cell)); diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index ed6e9f8ee..1c7a7351f 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -146,7 +146,7 @@ struct FirrtlWorker if (!mask.is_fully_def()) this->ena = SigSpec(RTLIL::Const(1)); } - string gen_read(const char * /* indent */) { + string gen_read(const char * /* indent */) { log_error("gen_read called on write_port: %s\n", name.c_str()); return stringf("gen_read called on write_port: %s\n", name.c_str()); } @@ -163,31 +163,61 @@ struct FirrtlWorker } }; /* Memories defined within this module. */ - struct memory { - string name; // memory name - int abits; // number of address bits - int size; // size (in units) of the memory - int width; // size (in bits) of each element - int read_latency; - int write_latency; - vector<read_port> read_ports; - vector<write_port> write_ports; - std::string init_file; - std::string init_file_srcFileSpec; - memory(string name, int abits, int size, int width) : name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") {} - memory() : read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){} - void add_memory_read_port(read_port &rp) { - read_ports.push_back(rp); - } - void add_memory_write_port(write_port &wp) { - write_ports.push_back(wp); - } - void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) { - this->init_file = init_file; - this->init_file_srcFileSpec = init_file_srcFileSpec; + struct memory { + Cell *pCell; // for error reporting + string name; // memory name + int abits; // number of address bits + int size; // size (in units) of the memory + int width; // size (in bits) of each element + int read_latency; + int write_latency; + vector<read_port> read_ports; + vector<write_port> write_ports; + std::string init_file; + std::string init_file_srcFileSpec; + string srcLine; + memory(Cell *pCell, string name, int abits, int size, int width) : pCell(pCell), name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") { + // Provide defaults for abits or size if one (but not the other) is specified. + if (this->abits == 0 && this->size != 0) { + this->abits = ceil_log2(this->size); + } else if (this->abits != 0 && this->size == 0) { + this->size = 1 << this->abits; + } + // Sanity-check this construction. + if (this->name == "") { + log_error("Nameless memory%s\n", this->atLine()); + } + if (this->abits == 0 && this->size == 0) { + log_error("Memory %s has zero address bits and size%s\n", this->name.c_str(), this->atLine()); + } + if (this->width == 0) { + log_error("Memory %s has zero width%s\n", this->name.c_str(), this->atLine()); + } } + // We need a default constructor for the dict insert. + memory() : pCell(0), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){} + + const char *atLine() { + if (srcLine == "") { + if (pCell) { + auto p = pCell->attributes.find("\\src"); + srcLine = " at " + p->second.decode_string(); + } + } + return srcLine.c_str(); + } + void add_memory_read_port(read_port &rp) { + read_ports.push_back(rp); + } + void add_memory_write_port(write_port &wp) { + write_ports.push_back(wp); + } + void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) { + this->init_file = init_file; + this->init_file_srcFileSpec = init_file_srcFileSpec; + } - }; + }; dict<string, memory> memories; void register_memory(memory &m) @@ -314,6 +344,7 @@ struct FirrtlWorker switch (dir) { case FD_INOUT: log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", cell_type.c_str(), log_signal(it->second)); + /* FALLTHRU */ case FD_OUT: sourceExpr = firstName; sinkExpr = secondExpr; @@ -321,7 +352,7 @@ struct FirrtlWorker break; case FD_NODIRECTION: log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", cell_type.c_str(), log_signal(it->second)); - /* FALL_THROUGH */ + /* FALLTHRU */ case FD_IN: sourceExpr = secondExpr; sinkExpr = firstName; @@ -418,8 +449,10 @@ struct FirrtlWorker string primop; bool always_uint = false; if (cell->type == "$not") primop = "not"; - else if (cell->type == "$neg") primop = "neg"; - else if (cell->type == "$logic_not") { + else if (cell->type == "$neg") { + primop = "neg"; + is_signed = true; // Result of "neg" is signed (an SInt). + } else if (cell->type == "$logic_not") { primop = "eq"; a_expr = stringf("%s, UInt(0)", a_expr.c_str()); } @@ -531,6 +564,7 @@ struct FirrtlWorker auto b_sig = cell->getPort("\\B"); if (b_sig.is_fully_const()) { primop = "shl"; + b_expr = std::to_string(b_sig.as_int()); } else { primop = "dshl"; // Convert from FIRRTL left shift semantics. @@ -544,6 +578,7 @@ struct FirrtlWorker auto b_sig = cell->getPort("\\B"); if (b_sig.is_fully_const()) { primop = "shr"; + b_expr = std::to_string(b_sig.as_int()); } else { primop = "dshr"; } @@ -604,7 +639,7 @@ struct FirrtlWorker int abits = cell->parameters.at("\\ABITS").as_int(); int width = cell->parameters.at("\\WIDTH").as_int(); int size = cell->parameters.at("\\SIZE").as_int(); - memory m(mem_id, abits, size, width); + memory m(cell, mem_id, abits, size, width); int rd_ports = cell->parameters.at("\\RD_PORTS").as_int(); int wr_ports = cell->parameters.at("\\WR_PORTS").as_int(); @@ -681,6 +716,8 @@ struct FirrtlWorker { std::string cell_type = fid(cell->type); std::string mem_id = make_id(cell->parameters["\\MEMID"].decode_string()); + int abits = cell->parameters.at("\\ABITS").as_int(); + int width = cell->parameters.at("\\WIDTH").as_int(); memory *mp = nullptr; if (cell->type == "$meminit" ) { log_error("$meminit (%s.%s.%s) currently unsupported\n", log_id(module), log_id(cell), mem_id.c_str()); @@ -693,6 +730,11 @@ struct FirrtlWorker Const clk_enable = cell->parameters.at("\\CLK_ENABLE"); Const clk_polarity = cell->parameters.at("\\CLK_POLARITY"); + // Do we already have an entry for this memory? + if (memories.count(mem_id) == 0) { + memory m(cell, mem_id, abits, 0, width); + register_memory(m); + } mp = &memories.at(mem_id); int portNum = 0; bool transparency = false; @@ -890,7 +932,7 @@ struct FirrtlWorker // If we have any memory definitions, output them. for (auto kv : memories) { - memory m = kv.second; + memory &m = kv.second; f << stringf(" mem %s:\n", m.name.c_str()); f << stringf(" data-type => UInt<%d>\n", m.width); f << stringf(" depth => %d\n", m.size); diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc index dc39e5e08..04d1ee311 100644 --- a/backends/ilang/ilang_backend.cc +++ b/backends/ilang/ilang_backend.cc @@ -160,7 +160,10 @@ void ILANG_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL:: } f << stringf("%s" "cell %s %s\n", indent.c_str(), cell->type.c_str(), cell->name.c_str()); for (auto &it : cell->parameters) { - f << stringf("%s parameter%s %s ", indent.c_str(), (it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "", it.first.c_str()); + f << stringf("%s parameter%s%s %s ", indent.c_str(), + (it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "", + (it.second.flags & RTLIL::CONST_FLAG_REAL) != 0 ? " real" : "", + it.first.c_str()); dump_const(f, it.second); f << stringf("\n"); } diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 9967482d6..827af5d85 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -183,8 +183,9 @@ bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name) return true; } -void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool set_signed = false, bool escape_comment = false) +void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool escape_comment = false) { + bool set_signed = (data.flags & RTLIL::CONST_FLAG_SIGNED) != 0; if (width < 0) width = data.bits.size() - offset; if (width == 0) { @@ -275,7 +276,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o } } } else { - f << stringf("\""); + if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0) + f << stringf("\""); std::string str = data.decode_string(); for (size_t i = 0; i < str.size(); i++) { if (str[i] == '\n') @@ -293,7 +295,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o else f << str[i]; } - f << stringf("\""); + if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0) + f << stringf("\""); } } @@ -373,7 +376,7 @@ void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, else if (modattr && (it->second == Const(1, 1) || it->second == Const(1))) f << stringf(" 1 "); else - dump_const(f, it->second, -1, 0, false, false, attr2comment); + dump_const(f, it->second, -1, 0, false, attr2comment); f << stringf(" %s%c", attr2comment ? "*/" : "*)", term); } } @@ -1242,6 +1245,118 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } + if (cell->type.in("$assert", "$assume", "$cover")) + { + f << stringf("%s" "always @* if (", indent.c_str()); + dump_sigspec(f, cell->getPort("\\EN")); + f << stringf(") %s(", cell->type.c_str()+1); + dump_sigspec(f, cell->getPort("\\A")); + f << stringf(");\n"); + return true; + } + + if (cell->type.in("$specify2", "$specify3")) + { + f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str()); + + SigSpec en = cell->getPort("\\EN"); + if (en != State::S1) { + f << stringf("if ("); + dump_sigspec(f, cell->getPort("\\EN")); + f << stringf(") "); + } + + f << "("; + if (cell->type == "$specify3" && cell->getParam("\\EDGE_EN").as_bool()) + f << (cell->getParam("\\EDGE_POL").as_bool() ? "posedge ": "negedge "); + + dump_sigspec(f, cell->getPort("\\SRC")); + + f << " "; + if (cell->getParam("\\SRC_DST_PEN").as_bool()) + f << (cell->getParam("\\SRC_DST_POL").as_bool() ? "+": "-"); + f << (cell->getParam("\\FULL").as_bool() ? "*> ": "=> "); + + if (cell->type == "$specify3") { + f << "("; + dump_sigspec(f, cell->getPort("\\DST")); + f << " "; + if (cell->getParam("\\DAT_DST_PEN").as_bool()) + f << (cell->getParam("\\DAT_DST_POL").as_bool() ? "+": "-"); + f << ": "; + dump_sigspec(f, cell->getPort("\\DAT")); + f << ")"; + } else { + dump_sigspec(f, cell->getPort("\\DST")); + } + + bool bak_decimal = decimal; + decimal = 1; + + f << ") = ("; + dump_const(f, cell->getParam("\\T_RISE_MIN")); + f << ":"; + dump_const(f, cell->getParam("\\T_RISE_TYP")); + f << ":"; + dump_const(f, cell->getParam("\\T_RISE_MAX")); + f << ", "; + dump_const(f, cell->getParam("\\T_FALL_MIN")); + f << ":"; + dump_const(f, cell->getParam("\\T_FALL_TYP")); + f << ":"; + dump_const(f, cell->getParam("\\T_FALL_MAX")); + f << ");\n"; + + decimal = bak_decimal; + + f << stringf("%s" "endspecify\n", indent.c_str()); + return true; + } + + if (cell->type == "$specrule") + { + f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str()); + + string spec_type = cell->getParam("\\TYPE").decode_string(); + f << stringf("%s(", spec_type.c_str()); + + if (cell->getParam("\\SRC_PEN").as_bool()) + f << (cell->getParam("\\SRC_POL").as_bool() ? "posedge ": "negedge "); + dump_sigspec(f, cell->getPort("\\SRC")); + + if (cell->getPort("\\SRC_EN") != State::S1) { + f << " &&& "; + dump_sigspec(f, cell->getPort("\\SRC_EN")); + } + + f << ", "; + if (cell->getParam("\\DST_PEN").as_bool()) + f << (cell->getParam("\\DST_POL").as_bool() ? "posedge ": "negedge "); + dump_sigspec(f, cell->getPort("\\DST")); + + if (cell->getPort("\\DST_EN") != State::S1) { + f << " &&& "; + dump_sigspec(f, cell->getPort("\\DST_EN")); + } + + bool bak_decimal = decimal; + decimal = 1; + + f << ", "; + dump_const(f, cell->getParam("\\T_LIMIT")); + + if (spec_type == "$setuphold" || spec_type == "$recrem" || spec_type == "$fullskew") { + f << ", "; + dump_const(f, cell->getParam("\\T_LIMIT2")); + } + + f << ");\n"; + decimal = bak_decimal; + + f << stringf("%s" "endspecify\n", indent.c_str()); + return true; + } + // FIXME: $_SR_[PN][PN]_, $_DLATCH_[PN]_, $_DLATCHSR_[PN][PN][PN]_ // FIXME: $sr, $dlatch, $memrd, $memwr, $fsm @@ -1264,8 +1379,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (it != cell->parameters.begin()) f << stringf(","); f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str()); - bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0; - dump_const(f, it->second, -1, 0, false, is_signed); + dump_const(f, it->second); f << stringf(")"); } f << stringf("\n%s" ")", indent.c_str()); @@ -1312,8 +1426,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (defparam && cell->parameters.size() > 0) { for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) { f << stringf("%sdefparam %s.%s = ", indent.c_str(), cell_name.c_str(), id(it->first).c_str()); - bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0; - dump_const(f, it->second, -1, 0, false, is_signed); + dump_const(f, it->second); f << stringf(";\n"); } } @@ -1505,7 +1618,8 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) SigSpec sig = active_sigmap(wire); Const val = wire->attributes.at("\\init"); for (int i = 0; i < GetSize(sig) && i < GetSize(val); i++) - active_initdata[sig[i]] = val.bits.at(i); + if (val[i] == State::S0 || val[i] == State::S1) + active_initdata[sig[i]] = val[i]; } if (!module->processes.empty()) diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index 904a1079d..4c19ec171 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -36,907 +36,881 @@ YOSYS_NAMESPACE_BEGIN AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports) - : design(design), f(f), clk_name(clk_name), map_filename(map_filename), wideports(wideports) + : design(design), f(f), clk_name(clk_name), map_filename(map_filename), wideports(wideports) { - module = new RTLIL::Module; - module->name = module_name; - if (design->module(module->name)) - log_error("Duplicate definition of module %s!\n", log_id(module->name)); + module = new RTLIL::Module; + module->name = module_name; + if (design->module(module->name)) + log_error("Duplicate definition of module %s!\n", log_id(module->name)); } void AigerReader::parse_aiger() { - std::string header; - f >> header; - if (header != "aag" && header != "aig") - log_error("Unsupported AIGER file!\n"); - - // Parse rest of header - if (!(f >> M >> I >> L >> O >> A)) - log_error("Invalid AIGER header\n"); - - // Optional values - B = C = J = F = 0; - for (auto &i : std::array<std::reference_wrapper<unsigned>,4>{B, C, J, F}) { - if (f.peek() != ' ') break; - if (!(f >> i)) - log_error("Invalid AIGER header\n"); - } - - std::string line; - std::getline(f, line); // Ignore up to start of next line, as standard - // says anything that follows could be used for - // optional sections - - log_debug("M=%u I=%u L=%u O=%u A=%u B=%u C=%u J=%u F=%u\n", M, I, L, O, A, B, C, J, F); - - line_count = 1; - - if (header == "aag") - parse_aiger_ascii(); - else if (header == "aig") - parse_aiger_binary(); - else - log_abort(); - - // Parse footer (symbol table, comments, etc.) - unsigned l1; - std::string s; - for (int c = f.peek(); c != EOF; c = f.peek(), ++line_count) { - if (c == 'i' || c == 'l' || c == 'o') { - f.ignore(1); - if (!(f >> l1 >> s)) - log_error("Line %u cannot be interpreted as a symbol entry!\n", line_count); - - if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size())) - log_error("Line %u has invalid symbol position!\n", line_count); - - RTLIL::Wire* wire; - if (c == 'i') wire = inputs[l1]; - else if (c == 'l') wire = latches[l1]; - else if (c == 'o') wire = outputs[l1]; - else log_abort(); - - module->rename(wire, stringf("\\%s", s.c_str())); - } - else if (c == 'b' || c == 'j' || c == 'f') { - // TODO - } - else if (c == 'c') { - f.ignore(1); - if (f.peek() == '\n') - break; - // Else constraint (TODO) - } - else - log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); - std::getline(f, line); // Ignore up to start of next line - } - - dict<RTLIL::IdString, int> wideports_cache; - - if (!map_filename.empty()) { - std::ifstream mf(map_filename); - std::string type, symbol; - int variable, index; - while (mf >> type >> variable >> index >> symbol) { - RTLIL::IdString escaped_symbol = RTLIL::escape_id(symbol); - if (type == "input") { - log_assert(static_cast<unsigned>(variable) < inputs.size()); - RTLIL::Wire* wire = inputs[variable]; - log_assert(wire); - log_assert(wire->port_input); - - if (index == 0) - module->rename(wire, RTLIL::escape_id(symbol)); - else if (index > 0) { - module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", symbol.c_str(), index))); - if (wideports) - wideports_cache[escaped_symbol] = std::max(wideports_cache[escaped_symbol], index); - } - } - else if (type == "output") { - log_assert(static_cast<unsigned>(variable) < outputs.size()); - RTLIL::Wire* wire = outputs[variable]; - log_assert(wire); - // Ignore direct output -> input connections - if (!wire->port_output) - continue; - log_assert(wire->port_output); - - if (index == 0) - module->rename(wire, RTLIL::escape_id(symbol)); - else if (index > 0) { - module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", symbol.c_str(), index))); - if (wideports) - wideports_cache[escaped_symbol] = std::max(wideports_cache[escaped_symbol], index); - } - } - else - log_error("Symbol type '%s' not recognised.\n", type.c_str()); - } - } - - for (auto &wp : wideports_cache) { - auto name = wp.first; - int width = wp.second + 1; - - RTLIL::Wire *wire = module->wire(name); - if (wire) - module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", name.c_str(), 0))); - - // Do not make ports with a mix of input/output into - // wide ports - bool port_input = false, port_output = false; - for (int i = 0; i < width; i++) { - RTLIL::IdString other_name = name.str() + stringf("[%d]", i); - RTLIL::Wire *other_wire = module->wire(other_name); - if (other_wire) { - port_input = port_input || other_wire->port_input; - port_output = port_output || other_wire->port_output; - } - } - if ((port_input && port_output) || (!port_input && !port_output)) - continue; - - wire = module->addWire(name, width); - wire->port_input = port_input; - wire->port_output = port_output; - - for (int i = 0; i < width; i++) { - RTLIL::IdString other_name = name.str() + stringf("[%d]", i); - RTLIL::Wire *other_wire = module->wire(other_name); - if (other_wire) { - other_wire->port_input = false; - other_wire->port_output = false; - if (wire->port_input) - module->connect(other_wire, SigSpec(wire, i)); - else - module->connect(SigSpec(wire, i), other_wire); - } - } - } - - module->fixup_ports(); - design->add(module); - - Pass::call(design, "clean"); - - for (auto cell : module->cells().to_vector()) { - if (cell->type != "$lut") continue; - auto y_port = cell->getPort("\\Y").as_bit(); - if (y_port.wire->width == 1) - module->rename(cell, stringf("%s$lut", y_port.wire->name.c_str())); - else - module->rename(cell, stringf("%s[%d]$lut", y_port.wire->name.c_str(), y_port.offset)); - } + std::string header; + f >> header; + if (header != "aag" && header != "aig") + log_error("Unsupported AIGER file!\n"); + + // Parse rest of header + if (!(f >> M >> I >> L >> O >> A)) + log_error("Invalid AIGER header\n"); + + // Optional values + B = C = J = F = 0; + if (f.peek() != ' ') goto end_of_header; + if (!(f >> B)) log_error("Invalid AIGER header\n"); + if (f.peek() != ' ') goto end_of_header; + if (!(f >> C)) log_error("Invalid AIGER header\n"); + if (f.peek() != ' ') goto end_of_header; + if (!(f >> J)) log_error("Invalid AIGER header\n"); + if (f.peek() != ' ') goto end_of_header; + if (!(f >> F)) log_error("Invalid AIGER header\n"); +end_of_header: + + std::string line; + std::getline(f, line); // Ignore up to start of next line, as standard + // says anything that follows could be used for + // optional sections + + log_debug("M=%u I=%u L=%u O=%u A=%u B=%u C=%u J=%u F=%u\n", M, I, L, O, A, B, C, J, F); + + line_count = 1; + piNum = 0; + flopNum = 0; + + if (header == "aag") + parse_aiger_ascii(); + else if (header == "aig") + parse_aiger_binary(); + else + log_abort(); + + RTLIL::Wire* n0 = module->wire("\\__0__"); + if (n0) + module->connect(n0, RTLIL::S0); + + // Parse footer (symbol table, comments, etc.) + unsigned l1; + std::string s; + for (int c = f.peek(); c != EOF; c = f.peek(), ++line_count) { + if (c == 'i' || c == 'l' || c == 'o' || c == 'b') { + f.ignore(1); + if (!(f >> l1 >> s)) + log_error("Line %u cannot be interpreted as a symbol entry!\n", line_count); + + if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size())) + log_error("Line %u has invalid symbol position!\n", line_count); + + RTLIL::Wire* wire; + if (c == 'i') wire = inputs[l1]; + else if (c == 'l') wire = latches[l1]; + else if (c == 'o') wire = outputs[l1]; + else if (c == 'b') wire = bad_properties[l1]; + else log_abort(); + + module->rename(wire, stringf("\\%s", s.c_str())); + } + else if (c == 'j' || c == 'f') { + // TODO + } + else if (c == 'c') { + f.ignore(1); + if (f.peek() == '\n') + break; + // Else constraint (TODO) + } + else + log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); + std::getline(f, line); // Ignore up to start of next line + } + + post_process(); } static uint32_t parse_xaiger_literal(std::istream &f) { - uint32_t l; - f.read(reinterpret_cast<char*>(&l), sizeof(l)); - if (f.gcount() != sizeof(l)) - log_error("Offset %ld: unable to read literal!\n", static_cast<int64_t>(f.tellg())); - // TODO: Don't assume we're on little endian + uint32_t l; + f.read(reinterpret_cast<char*>(&l), sizeof(l)); + if (f.gcount() != sizeof(l)) + log_error("Offset %ld: unable to read literal!\n", static_cast<int64_t>(f.tellg())); + // TODO: Don't assume we're on little endian #ifdef _WIN32 - return _byteswap_ulong(l); + return _byteswap_ulong(l); #else - return __builtin_bswap32(l); + return __builtin_bswap32(l); #endif } static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal) { - const unsigned variable = literal >> 1; - const bool invert = literal & 1; - RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix? - RTLIL::Wire *wire = module->wire(wire_name); - if (wire) return wire; - log_debug("Creating %s\n", wire_name.c_str()); - wire = module->addWire(wire_name); - wire->port_input = wire->port_output = false; - if (!invert) return wire; - RTLIL::IdString wire_inv_name(stringf("\\__%d__", variable)); - RTLIL::Wire *wire_inv = module->wire(wire_inv_name); - if (wire_inv) { - if (module->cell(wire_inv_name)) return wire; - } - else { - log_debug("Creating %s\n", wire_inv_name.c_str()); - wire_inv = module->addWire(wire_inv_name); - wire_inv->port_input = wire_inv->port_output = false; - } - - log_debug("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str()); - module->addNotGate(stringf("\\__%d__$not", variable), wire_inv, wire); // FIXME: is "$not" the right suffix? - - return wire; -} - -static std::pair<RTLIL::IdString, int> wideports_split(std::string name) -{ - int pos = -1; - - if (name.empty() || name.back() != ']') - goto failed; - - for (int i = 0; i+1 < GetSize(name); i++) { - if (name[i] == '[') - pos = i; - else if (name[i] < '0' || name[i] > '9') - pos = -1; - else if (i == pos+1 && name[i] == '0' && name[i+1] != ']') - pos = -1; + const unsigned variable = literal >> 1; + const bool invert = literal & 1; + RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix? + RTLIL::Wire *wire = module->wire(wire_name); + if (wire) return wire; + log_debug("Creating %s\n", wire_name.c_str()); + wire = module->addWire(wire_name); + wire->port_input = wire->port_output = false; + if (!invert) return wire; + RTLIL::IdString wire_inv_name(stringf("\\__%d__", variable)); + RTLIL::Wire *wire_inv = module->wire(wire_inv_name); + if (wire_inv) { + if (module->cell(wire_inv_name)) return wire; + } + else { + log_debug("Creating %s\n", wire_inv_name.c_str()); + wire_inv = module->addWire(wire_inv_name); + wire_inv->port_input = wire_inv->port_output = false; } - if (pos >= 0) - return std::pair<RTLIL::IdString, int>(RTLIL::escape_id(name.substr(0, pos)), atoi(name.c_str() + pos+1)); + log_debug("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str()); + module->addNotGate(stringf("\\__%d__$not", variable), wire_inv, wire); // FIXME: is "$not" the right suffix? -failed: - return std::pair<RTLIL::IdString, int>(name, 0); + return wire; } void AigerReader::parse_xaiger() { - std::string header; - f >> header; - if (header != "aag" && header != "aig") - log_error("Unsupported AIGER file!\n"); - - // Parse rest of header - if (!(f >> M >> I >> L >> O >> A)) - log_error("Invalid AIGER header\n"); - - // Optional values - B = C = J = F = 0; - - std::string line; - std::getline(f, line); // Ignore up to start of next line, as standard - // says anything that follows could be used for - // optional sections - - log_debug("M=%u I=%u L=%u O=%u A=%u\n", M, I, L, O, A); - - line_count = 1; - - if (header == "aag") - parse_aiger_ascii(); - else if (header == "aig") - parse_aiger_binary(); - else - log_abort(); - - // Parse footer (symbol table, comments, etc.) - unsigned l1; - std::string s; - bool comment_seen = false; - std::vector<std::pair<RTLIL::Wire*,RTLIL::IdString>> deferred_renames; - std::vector<std::pair<RTLIL::Wire*,RTLIL::IdString>> deferred_inouts; - deferred_renames.reserve(inputs.size() + latches.size() + outputs.size()); - for (int c = f.peek(); c != EOF; c = f.peek()) { - if (comment_seen || c == 'c') { - if (!comment_seen) { - f.ignore(1); - c = f.peek(); - comment_seen = true; - } - if (c == '\n') - break; - f.ignore(1); - // XAIGER extensions - if (c == 'm') { - uint32_t dataSize = parse_xaiger_literal(f); - uint32_t lutNum = parse_xaiger_literal(f); - uint32_t lutSize = parse_xaiger_literal(f); - log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize); - ConstEval ce(module); - for (unsigned i = 0; i < lutNum; ++i) { - uint32_t rootNodeID = parse_xaiger_literal(f); - uint32_t cutLeavesM = parse_xaiger_literal(f); - log_debug("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM); - RTLIL::Wire *output_sig = module->wire(stringf("\\__%d__", rootNodeID)); - uint32_t nodeID; - RTLIL::SigSpec input_sig; - for (unsigned j = 0; j < cutLeavesM; ++j) { - nodeID = parse_xaiger_literal(f); - log_debug("\t%u\n", nodeID); - RTLIL::Wire *wire = module->wire(stringf("\\__%d__", nodeID)); - log_assert(wire); - input_sig.append(wire); - } - RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << input_sig.size()); - for (int j = 0; j < (1 << cutLeavesM); ++j) { - ce.push(); - ce.set(input_sig, RTLIL::Const{j, static_cast<int>(cutLeavesM)}); - RTLIL::SigSpec o(output_sig); - ce.eval(o); - lut_mask[j] = o.as_const()[0]; - ce.pop(); - } - RTLIL::Cell *output_cell = module->cell(stringf("\\__%d__$and", rootNodeID)); - log_assert(output_cell); - module->remove(output_cell); - module->addLut(stringf("\\__%d__$lut", rootNodeID), input_sig, output_sig, std::move(lut_mask)); - } - } - else if (c == 'r') { - /*uint32_t dataSize =*/ parse_xaiger_literal(f); - uint32_t flopNum = parse_xaiger_literal(f); - f.ignore(flopNum * sizeof(uint32_t)); - log_assert(inputs.size() >= flopNum); - for (auto it = inputs.end() - flopNum; it != inputs.end(); ++it) { - log_assert((*it)->port_input); - (*it)->port_input = false; - } - inputs.erase(inputs.end() - flopNum, inputs.end()); - log_assert(outputs.size() >= flopNum); - for (auto it = outputs.end() - flopNum; it != outputs.end(); ++it) { - log_assert((*it)->port_output); - (*it)->port_output = false; - } - outputs.erase(outputs.end() - flopNum, outputs.end()); - module->fixup_ports(); - } - else if (c == 'n') { - parse_xaiger_literal(f); - f >> s; - log_debug("n: '%s'\n", s.c_str()); - } - else if (c == 'a' || c == 'i' || c == 'o' || c == 'h') { - uint32_t dataSize = parse_xaiger_literal(f); - f.ignore(dataSize); - } - else { - break; - } - } - else if (c == 'i' || c == 'l' || c == 'o') { - f.ignore(1); - if (!(f >> l1 >> s)) - log_error("Line %u cannot be interpreted as a symbol entry!\n", line_count); - - if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size())) - log_error("Line %u has invalid symbol position!\n", line_count); - - RTLIL::Wire* wire; - if (c == 'i') wire = inputs[l1]; - else if (c == 'l') wire = latches[l1]; - else if (c == 'o') wire = outputs[l1]; - else log_abort(); - - RTLIL::IdString escaped_s = RTLIL::escape_id(s); - - if (escaped_s.ends_with("$inout.out")) { - deferred_inouts.emplace_back(wire, escaped_s.substr(0, escaped_s.size()-10)); - goto next_line; - } - else if (wideports && (wire->port_input || wire->port_output)) { - RTLIL::IdString wide_symbol; - int index; - std::tie(wide_symbol,index) = wideports_split(escaped_s.str()); - if (wide_symbol.ends_with("$inout.out")) { - deferred_inouts.emplace_back(wire, stringf("%s[%d]", wide_symbol.substr(0, wide_symbol.size()-10).c_str(), index)); - goto next_line; - } - } - deferred_renames.emplace_back(wire, escaped_s); - -next_line: - std::getline(f, line); // Ignore up to start of next line - ++line_count; - } - else - log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); - } - - dict<RTLIL::IdString, int> wideports_cache; - for (const auto &i : deferred_renames) { - RTLIL::Wire *wire = i.first; - - module->rename(wire, i.second); - - if (wideports && (wire->port_input || wire->port_output)) { - RTLIL::IdString escaped_symbol; - int index; - std::tie(escaped_symbol,index) = wideports_split(wire->name.str()); - if (index > 0) - wideports_cache[escaped_symbol] = std::max(wideports_cache[escaped_symbol], index); - } - } - - for (const auto &i : deferred_inouts) { - RTLIL::Wire *out_wire = i.first; - log_assert(out_wire->port_output); - out_wire->port_output = false; - RTLIL::Wire *wire = module->wire(i.second); - log_assert(wire); - log_assert(wire->port_input && !wire->port_output); - wire->port_output = true; - module->connect(wire, out_wire); - } - - if (!map_filename.empty()) { - std::ifstream mf(map_filename); - std::string type, symbol; - int variable, index; - while (mf >> type >> variable >> index >> symbol) { - RTLIL::IdString escaped_s = RTLIL::escape_id(symbol); - if (type == "input") { - log_assert(static_cast<unsigned>(variable) < inputs.size()); - RTLIL::Wire* wire = inputs[variable]; - log_assert(wire); - log_assert(wire->port_input); - - if (index == 0) { - // Cope with the fact that a CI might be identical - // to a PI (necessary due to ABC); in those cases - // simply connect the latter to the former - RTLIL::Wire* existing = module->wire(escaped_s); - if (!existing) - module->rename(wire, escaped_s); - else { - wire->port_input = false; - module->connect(wire, existing); - } - } - else if (index > 0) { - std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); - RTLIL::Wire* existing = module->wire(indexed_name); - if (!existing) { - module->rename(wire, indexed_name); - if (wideports) - wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index); - } - else { - module->connect(wire, existing); - wire->port_input = false; - } - } - } - else if (type == "output") { - log_assert(static_cast<unsigned>(variable) < outputs.size()); - RTLIL::Wire* wire = outputs[variable]; - log_assert(wire); - log_assert(wire->port_output); - if (escaped_s.in("\\__dummy_o__", "\\__const0__", "\\__const1__")) { - wire->port_output = false; - continue; - } - - if (index == 0) { - // Cope with the fact that a CO might be identical - // to a PO (necessary due to ABC); in those cases - // simply connect the latter to the former - RTLIL::Wire* existing = module->wire(escaped_s); - if (!existing) { - if (escaped_s.ends_with("$inout.out")) { - wire->port_output = false; - RTLIL::Wire *in_wire = module->wire(escaped_s.substr(0, escaped_s.size()-10)); - log_assert(in_wire); - log_assert(in_wire->port_input && !in_wire->port_output); - in_wire->port_output = true; - module->connect(in_wire, wire); - } - else - module->rename(wire, escaped_s); - } - else { - wire->port_output = false; - module->connect(wire, existing); - } - } - else if (index > 0) { - std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); - RTLIL::Wire* existing = module->wire(indexed_name); - if (!existing) { - if (escaped_s.ends_with("$inout.out")) { - wire->port_output = false; - RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(0, escaped_s.size()-10).c_str(), index)); - log_assert(in_wire); - log_assert(in_wire->port_input && !in_wire->port_output); - in_wire->port_output = true; - module->connect(in_wire, wire); - } - else { - module->rename(wire, indexed_name); - if (wideports) - wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index); - } - } - else { - module->connect(wire, existing); - wire->port_output = false; - } - } - } - else - log_error("Symbol type '%s' not recognised.\n", type.c_str()); - } - } - - for (auto &wp : wideports_cache) { - auto name = wp.first; - int width = wp.second + 1; - - RTLIL::Wire *wire = module->wire(name); - if (wire) - module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", name.c_str(), 0))); - - // Do not make ports with a mix of input/output into - // wide ports - bool port_input = false, port_output = false; - for (int i = 0; i < width; i++) { - RTLIL::IdString other_name = name.str() + stringf("[%d]", i); - RTLIL::Wire *other_wire = module->wire(other_name); - if (other_wire) { - port_input = port_input || other_wire->port_input; - port_output = port_output || other_wire->port_output; - } - } - if ((port_input && port_output) || (!port_input && !port_output)) - continue; - - wire = module->addWire(name, width); - wire->port_input = port_input; - wire->port_output = port_output; - - for (int i = 0; i < width; i++) { - RTLIL::IdString other_name = name.str() + stringf("[%d]", i); - RTLIL::Wire *other_wire = module->wire(other_name); - if (other_wire) { - other_wire->port_input = false; - other_wire->port_output = false; - if (wire->port_input) - module->connect(other_wire, SigSpec(wire, i)); - else - module->connect(SigSpec(wire, i), other_wire); - } - } - } - - module->fixup_ports(); - design->add(module); - - for (auto cell : module->cells().to_vector()) { - if (cell->type != "$lut") continue; - auto y_port = cell->getPort("\\Y").as_bit(); - if (y_port.wire->width == 1) - module->rename(cell, stringf("%s$lut", y_port.wire->name.c_str())); - else - module->rename(cell, stringf("%s[%d]$lut", y_port.wire->name.c_str(), y_port.offset)); - } + std::string header; + f >> header; + if (header != "aag" && header != "aig") + log_error("Unsupported AIGER file!\n"); + + // Parse rest of header + if (!(f >> M >> I >> L >> O >> A)) + log_error("Invalid AIGER header\n"); + + // Optional values + B = C = J = F = 0; + + std::string line; + std::getline(f, line); // Ignore up to start of next line, as standard + // says anything that follows could be used for + // optional sections + + log_debug("M=%u I=%u L=%u O=%u A=%u\n", M, I, L, O, A); + + line_count = 1; + piNum = 0; + flopNum = 0; + + if (header == "aag") + parse_aiger_ascii(); + else if (header == "aig") + parse_aiger_binary(); + else + log_abort(); + + RTLIL::Wire* n0 = module->wire("\\__0__"); + if (n0) + module->connect(n0, RTLIL::S0); + + dict<int,IdString> box_lookup; + for (auto m : design->modules()) { + auto it = m->attributes.find("\\abc_box_id"); + if (it == m->attributes.end()) + continue; + if (m->name[0] == '$') continue; + auto r = box_lookup.insert(std::make_pair(it->second.as_int(), m->name)); + log_assert(r.second); + } + + // Parse footer (symbol table, comments, etc.) + std::string s; + bool comment_seen = false; + for (int c = f.peek(); c != EOF; c = f.peek()) { + if (comment_seen || c == 'c') { + if (!comment_seen) { + f.ignore(1); + c = f.peek(); + comment_seen = true; + } + if (c == '\n') + break; + f.ignore(1); + // XAIGER extensions + if (c == 'm') { + uint32_t dataSize = parse_xaiger_literal(f); + uint32_t lutNum = parse_xaiger_literal(f); + uint32_t lutSize = parse_xaiger_literal(f); + log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize); + ConstEval ce(module); + for (unsigned i = 0; i < lutNum; ++i) { + uint32_t rootNodeID = parse_xaiger_literal(f); + uint32_t cutLeavesM = parse_xaiger_literal(f); + log_debug("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM); + RTLIL::Wire *output_sig = module->wire(stringf("\\__%d__", rootNodeID)); + uint32_t nodeID; + RTLIL::SigSpec input_sig; + for (unsigned j = 0; j < cutLeavesM; ++j) { + nodeID = parse_xaiger_literal(f); + log_debug("\t%u\n", nodeID); + RTLIL::Wire *wire = module->wire(stringf("\\__%d__", nodeID)); + log_assert(wire); + input_sig.append(wire); + } + RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << input_sig.size()); + for (int j = 0; j < (1 << cutLeavesM); ++j) { + ce.push(); + ce.set(input_sig, RTLIL::Const{j, static_cast<int>(cutLeavesM)}); + RTLIL::SigSpec o(output_sig); + ce.eval(o); + lut_mask[j] = o.as_const()[0]; + ce.pop(); + } + RTLIL::Cell *output_cell = module->cell(stringf("\\__%d__$and", rootNodeID)); + log_assert(output_cell); + module->remove(output_cell); + module->addLut(stringf("\\__%d__$lut", rootNodeID), input_sig, output_sig, std::move(lut_mask)); + } + } + else if (c == 'r') { + uint32_t dataSize = parse_xaiger_literal(f); + flopNum = parse_xaiger_literal(f); + log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); + f.ignore(flopNum * sizeof(uint32_t)); + } + else if (c == 'n') { + parse_xaiger_literal(f); + f >> s; + log_debug("n: '%s'\n", s.c_str()); + } + else if (c == 'h') { + f.ignore(sizeof(uint32_t)); + uint32_t version = parse_xaiger_literal(f); + log_assert(version == 1); + uint32_t ciNum = parse_xaiger_literal(f); + log_debug("ciNum = %u\n", ciNum); + uint32_t coNum = parse_xaiger_literal(f); + log_debug("coNum = %u\n", coNum); + piNum = parse_xaiger_literal(f); + log_debug("piNum = %u\n", piNum); + uint32_t poNum = parse_xaiger_literal(f); + log_debug("poNum = %u\n", poNum); + uint32_t boxNum = parse_xaiger_literal(f); + log_debug("boxNum = %u\n", poNum); + for (unsigned i = 0; i < boxNum; i++) { + f.ignore(2*sizeof(uint32_t)); + uint32_t boxUniqueId = parse_xaiger_literal(f); + log_assert(boxUniqueId > 0); + uint32_t oldBoxNum = parse_xaiger_literal(f); + RTLIL::Cell* cell = module->addCell(stringf("$__box%u__", oldBoxNum), box_lookup.at(boxUniqueId)); + boxes.emplace_back(cell); + } + } + else if (c == 'a' || c == 'i' || c == 'o') { + uint32_t dataSize = parse_xaiger_literal(f); + f.ignore(dataSize); + } + else { + break; + } + } + else + log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); + } + + post_process(); } void AigerReader::parse_aiger_ascii() { - std::string line; - std::stringstream ss; - - unsigned l1, l2, l3; - - // Parse inputs - for (unsigned i = 0; i < I; ++i, ++line_count) { - if (!(f >> l1)) - log_error("Line %u cannot be interpreted as an input!\n", line_count); - log_debug("%d is an input\n", l1); - log_assert(!(l1 & 1)); // Inputs can't be inverted - RTLIL::Wire *wire = createWireIfNotExists(module, l1); - wire->port_input = true; - inputs.push_back(wire); - } - - // Parse latches - RTLIL::Wire *clk_wire = nullptr; - if (L > 0) { - log_assert(clk_name != ""); - clk_wire = module->wire(clk_name); - log_assert(!clk_wire); - log_debug("Creating %s\n", clk_name.c_str()); - clk_wire = module->addWire(clk_name); - clk_wire->port_input = true; - clk_wire->port_output = false; - } - for (unsigned i = 0; i < L; ++i, ++line_count) { - if (!(f >> l1 >> l2)) - log_error("Line %u cannot be interpreted as a latch!\n", line_count); - log_debug("%d %d is a latch\n", l1, l2); - log_assert(!(l1 & 1)); // TODO: Latch outputs can't be inverted? - RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); - RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); - - module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire); - - // Reset logic is optional in AIGER 1.9 - if (f.peek() == ' ') { - if (!(f >> l3)) - log_error("Line %u cannot be interpreted as a latch!\n", line_count); - - if (l3 == 0 || l3 == 1) - q_wire->attributes["\\init"] = RTLIL::Const(l3); - else if (l3 == l1) { - //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx); - } - else - log_error("Line %u has invalid reset literal for latch!\n", line_count); - } - else { - // AIGER latches are assumed to be initialized to zero - q_wire->attributes["\\init"] = RTLIL::Const(0); - } - latches.push_back(q_wire); - } - - // Parse outputs - for (unsigned i = 0; i < O; ++i, ++line_count) { - if (!(f >> l1)) - log_error("Line %u cannot be interpreted as an output!\n", line_count); - - RTLIL::Wire *wire; - if (l1 == 0 || l1 == 1) { - wire = module->addWire(NEW_ID); - if (l1 == 0) - module->connect(wire, RTLIL::State::S0); - else if (l1 == 1) - module->connect(wire, RTLIL::State::S1); - else - log_abort(); - } - else { - log_debug("%d is an output\n", l1); - const unsigned variable = l1 >> 1; - const bool invert = l1 & 1; - RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix? - wire = module->wire(wire_name); - if (!wire) - wire = createWireIfNotExists(module, l1); - else { - if (wire->port_input || wire->port_output) { - RTLIL::Wire *new_wire = module->addWire(NEW_ID); - module->connect(new_wire, wire); - wire = new_wire; - } - } - } - wire->port_output = true; - outputs.push_back(wire); - } - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse bad state properties - for (unsigned i = 0; i < B; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse invariant constraints - for (unsigned i = 0; i < C; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse justice properties - for (unsigned i = 0; i < J; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse fairness constraints - for (unsigned i = 0; i < F; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // Parse AND - for (unsigned i = 0; i < A; ++i) { - if (!(f >> l1 >> l2 >> l3)) - log_error("Line %u cannot be interpreted as an AND!\n", line_count); - - log_debug("%d %d %d is an AND\n", l1, l2, l3); - log_assert(!(l1 & 1)); - RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); - RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); - RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); - module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire); - } - std::getline(f, line); // Ignore up to start of next line + std::string line; + std::stringstream ss; + + unsigned l1, l2, l3; + + // Parse inputs + for (unsigned i = 1; i <= I; ++i, ++line_count) { + if (!(f >> l1)) + log_error("Line %u cannot be interpreted as an input!\n", line_count); + log_debug("%d is an input\n", l1); + log_assert(!(l1 & 1)); // Inputs can't be inverted + RTLIL::Wire *wire = createWireIfNotExists(module, l1); + wire->port_input = true; + inputs.push_back(wire); + } + + // Parse latches + RTLIL::Wire *clk_wire = nullptr; + if (L > 0) { + log_assert(clk_name != ""); + clk_wire = module->wire(clk_name); + log_assert(!clk_wire); + log_debug("Creating %s\n", clk_name.c_str()); + clk_wire = module->addWire(clk_name); + clk_wire->port_input = true; + clk_wire->port_output = false; + } + for (unsigned i = 0; i < L; ++i, ++line_count) { + if (!(f >> l1 >> l2)) + log_error("Line %u cannot be interpreted as a latch!\n", line_count); + log_debug("%d %d is a latch\n", l1, l2); + log_assert(!(l1 & 1)); // TODO: Latch outputs can't be inverted? + RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); + RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); + + module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire); + + // Reset logic is optional in AIGER 1.9 + if (f.peek() == ' ') { + if (!(f >> l3)) + log_error("Line %u cannot be interpreted as a latch!\n", line_count); + + if (l3 == 0) + q_wire->attributes["\\init"] = RTLIL::S0; + else if (l3 == 1) + q_wire->attributes["\\init"] = RTLIL::S1; + else if (l3 == l1) { + //q_wire->attributes["\\init"] = RTLIL::Sx; + } + else + log_error("Line %u has invalid reset literal for latch!\n", line_count); + } + else { + // AIGER latches are assumed to be initialized to zero + q_wire->attributes["\\init"] = RTLIL::S0; + } + latches.push_back(q_wire); + } + + // Parse outputs + for (unsigned i = 0; i < O; ++i, ++line_count) { + if (!(f >> l1)) + log_error("Line %u cannot be interpreted as an output!\n", line_count); + + log_debug("%d is an output\n", l1); + const unsigned variable = l1 >> 1; + const bool invert = l1 & 1; + RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix? + RTLIL::Wire *wire = module->wire(wire_name); + if (!wire) + wire = createWireIfNotExists(module, l1); + else if (wire->port_input || wire->port_output) { + RTLIL::Wire *new_wire = module->addWire(NEW_ID); + module->connect(new_wire, wire); + wire = new_wire; + } + wire->port_output = true; + outputs.push_back(wire); + } + + // Parse bad properties + for (unsigned i = 0; i < B; ++i, ++line_count) { + if (!(f >> l1)) + log_error("Line %u cannot be interpreted as a bad state property!\n", line_count); + + log_debug("%d is a bad state property\n", l1); + RTLIL::Wire *wire = createWireIfNotExists(module, l1); + wire->port_output = true; + bad_properties.push_back(wire); + } + + // TODO: Parse invariant constraints + for (unsigned i = 0; i < C; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse justice properties + for (unsigned i = 0; i < J; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse fairness constraints + for (unsigned i = 0; i < F; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // Parse AND + for (unsigned i = 0; i < A; ++i) { + if (!(f >> l1 >> l2 >> l3)) + log_error("Line %u cannot be interpreted as an AND!\n", line_count); + + log_debug("%d %d %d is an AND\n", l1, l2, l3); + log_assert(!(l1 & 1)); + RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); + RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); + RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); + module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire); + } + std::getline(f, line); // Ignore up to start of next line } static unsigned parse_next_delta_literal(std::istream &f, unsigned ref) { - unsigned x = 0, i = 0; - unsigned char ch; - while ((ch = f.get()) & 0x80) - x |= (ch & 0x7f) << (7 * i++); - return ref - (x | (ch << (7 * i))); + unsigned x = 0, i = 0; + unsigned char ch; + while ((ch = f.get()) & 0x80) + x |= (ch & 0x7f) << (7 * i++); + return ref - (x | (ch << (7 * i))); } void AigerReader::parse_aiger_binary() { - unsigned l1, l2, l3; - std::string line; - - // Parse inputs - for (unsigned i = 1; i <= I; ++i) { - log_debug("%d is an input\n", i); - RTLIL::Wire *wire = createWireIfNotExists(module, i << 1); - wire->port_input = true; - log_assert(!wire->port_output); - inputs.push_back(wire); - } - - // Parse latches - RTLIL::Wire *clk_wire = nullptr; - if (L > 0) { - log_assert(clk_name != ""); - clk_wire = module->wire(clk_name); - log_assert(!clk_wire); - log_debug("Creating %s\n", clk_name.c_str()); - clk_wire = module->addWire(clk_name); - clk_wire->port_input = true; - clk_wire->port_output = false; - } - l1 = (I+1) * 2; - for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) { - if (!(f >> l2)) - log_error("Line %u cannot be interpreted as a latch!\n", line_count); - log_debug("%d %d is a latch\n", l1, l2); - RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); - RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); - - module->addDff(NEW_ID, clk_wire, d_wire, q_wire); - - // Reset logic is optional in AIGER 1.9 - if (f.peek() == ' ') { - if (!(f >> l3)) - log_error("Line %u cannot be interpreted as a latch!\n", line_count); - - if (l3 == 0 || l3 == 1) - q_wire->attributes["\\init"] = RTLIL::Const(l3); - else if (l3 == l1) { - //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx); - } - else - log_error("Line %u has invalid reset literal for latch!\n", line_count); - } - else { - // AIGER latches are assumed to be initialized to zero - q_wire->attributes["\\init"] = RTLIL::Const(0); - } - latches.push_back(q_wire); - } - - // Parse outputs - for (unsigned i = 0; i < O; ++i, ++line_count) { - if (!(f >> l1)) - log_error("Line %u cannot be interpreted as an output!\n", line_count); - - RTLIL::Wire *wire; - if (l1 == 0 || l1 == 1) { - wire = module->addWire(NEW_ID); - if (l1 == 0) - module->connect(wire, RTLIL::State::S0); - else if (l1 == 1) - module->connect(wire, RTLIL::State::S1); - else - log_abort(); - } - else { - log_debug("%d is an output\n", l1); - const unsigned variable = l1 >> 1; - const bool invert = l1 & 1; - RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "_inv" the right suffix? - wire = module->wire(wire_name); - if (!wire) - wire = createWireIfNotExists(module, l1); - else { - if (wire->port_input || wire->port_output) { - RTLIL::Wire *new_wire = module->addWire(NEW_ID); - module->connect(new_wire, wire); - wire = new_wire; - } - } - } - wire->port_output = true; - outputs.push_back(wire); - } - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse bad state properties - for (unsigned i = 0; i < B; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse invariant constraints - for (unsigned i = 0; i < C; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse justice properties - for (unsigned i = 0; i < J; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // TODO: Parse fairness constraints - for (unsigned i = 0; i < F; ++i, ++line_count) - std::getline(f, line); // Ignore up to start of next line - - // Parse AND - l1 = (I+L+1) << 1; - for (unsigned i = 0; i < A; ++i, ++line_count, l1 += 2) { - l2 = parse_next_delta_literal(f, l1); - l3 = parse_next_delta_literal(f, l2); - - log_debug("%d %d %d is an AND\n", l1, l2, l3); - log_assert(!(l1 & 1)); - RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); - RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); - RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); - module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire); - } + unsigned l1, l2, l3; + std::string line; + + // Parse inputs + for (unsigned i = 1; i <= I; ++i) { + log_debug("%d is an input\n", i); + RTLIL::Wire *wire = createWireIfNotExists(module, i << 1); + wire->port_input = true; + log_assert(!wire->port_output); + inputs.push_back(wire); + } + + // Parse latches + RTLIL::Wire *clk_wire = nullptr; + if (L > 0) { + log_assert(clk_name != ""); + clk_wire = module->wire(clk_name); + log_assert(!clk_wire); + log_debug("Creating %s\n", clk_name.c_str()); + clk_wire = module->addWire(clk_name); + clk_wire->port_input = true; + clk_wire->port_output = false; + } + l1 = (I+1) * 2; + for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) { + if (!(f >> l2)) + log_error("Line %u cannot be interpreted as a latch!\n", line_count); + log_debug("%d %d is a latch\n", l1, l2); + RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); + RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); + + module->addDff(NEW_ID, clk_wire, d_wire, q_wire); + + // Reset logic is optional in AIGER 1.9 + if (f.peek() == ' ') { + if (!(f >> l3)) + log_error("Line %u cannot be interpreted as a latch!\n", line_count); + + if (l3 == 0) + q_wire->attributes["\\init"] = RTLIL::S0; + else if (l3 == 1) + q_wire->attributes["\\init"] = RTLIL::S1; + else if (l3 == l1) { + //q_wire->attributes["\\init"] = RTLIL::Sx; + } + else + log_error("Line %u has invalid reset literal for latch!\n", line_count); + } + else { + // AIGER latches are assumed to be initialized to zero + q_wire->attributes["\\init"] = RTLIL::S0; + } + latches.push_back(q_wire); + } + + // Parse outputs + for (unsigned i = 0; i < O; ++i, ++line_count) { + if (!(f >> l1)) + log_error("Line %u cannot be interpreted as an output!\n", line_count); + + log_debug("%d is an output\n", l1); + const unsigned variable = l1 >> 1; + const bool invert = l1 & 1; + RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "_b" the right suffix? + RTLIL::Wire *wire = module->wire(wire_name); + if (!wire) + wire = createWireIfNotExists(module, l1); + else if (wire->port_input || wire->port_output) { + RTLIL::Wire *new_wire = module->addWire(NEW_ID); + module->connect(new_wire, wire); + wire = new_wire; + } + wire->port_output = true; + outputs.push_back(wire); + } + std::getline(f, line); // Ignore up to start of next line + + // Parse bad properties + for (unsigned i = 0; i < B; ++i, ++line_count) { + if (!(f >> l1)) + log_error("Line %u cannot be interpreted as a bad state property!\n", line_count); + + log_debug("%d is a bad state property\n", l1); + RTLIL::Wire *wire = createWireIfNotExists(module, l1); + wire->port_output = true; + bad_properties.push_back(wire); + } + if (B > 0) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse invariant constraints + for (unsigned i = 0; i < C; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse justice properties + for (unsigned i = 0; i < J; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse fairness constraints + for (unsigned i = 0; i < F; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // Parse AND + l1 = (I+L+1) << 1; + for (unsigned i = 0; i < A; ++i, ++line_count, l1 += 2) { + l2 = parse_next_delta_literal(f, l1); + l3 = parse_next_delta_literal(f, l2); + + log_debug("%d %d %d is an AND\n", l1, l2, l3); + log_assert(!(l1 & 1)); + RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); + RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); + RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); + module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire); + } +} + +void AigerReader::post_process() +{ + pool<RTLIL::Module*> abc_carry_modules; + unsigned ci_count = 0, co_count = 0, flop_count = 0; + for (auto cell : boxes) { + RTLIL::Module* box_module = design->module(cell->type); + log_assert(box_module); + + if (box_module->attributes.count("\\abc_carry") && !abc_carry_modules.count(box_module)) { + RTLIL::Wire* carry_in = nullptr, *carry_out = nullptr; + RTLIL::Wire* last_in = nullptr, *last_out = nullptr; + for (const auto &port_name : box_module->ports) { + RTLIL::Wire* w = box_module->wire(port_name); + log_assert(w); + if (w->port_input) { + if (w->attributes.count("\\abc_carry_in")) { + log_assert(!carry_in); + carry_in = w; + } + log_assert(!last_in || last_in->port_id < w->port_id); + last_in = w; + } + if (w->port_output) { + if (w->attributes.count("\\abc_carry_out")) { + log_assert(!carry_out); + carry_out = w; + } + log_assert(!last_out || last_out->port_id < w->port_id); + last_out = w; + } + } + + if (carry_in != last_in) { + std::swap(box_module->ports[carry_in->port_id], box_module->ports[last_in->port_id]); + std::swap(carry_in->port_id, last_in->port_id); + } + if (carry_out != last_out) { + log_assert(last_out); + std::swap(box_module->ports[carry_out->port_id], box_module->ports[last_out->port_id]); + std::swap(carry_out->port_id, last_out->port_id); + } + } + + bool flop = box_module->attributes.count("\\abc_flop"); + log_assert(!flop || flop_count < flopNum); + + // NB: Assume box_module->ports are sorted alphabetically + // (as RTLIL::Module::fixup_ports() would do) + for (auto port_name : box_module->ports) { + RTLIL::Wire* w = box_module->wire(port_name); + log_assert(w); + RTLIL::SigSpec rhs; + RTLIL::Wire* wire = nullptr; + for (int i = 0; i < GetSize(w); i++) { + if (w->port_input) { + log_assert(co_count < outputs.size()); + wire = outputs[co_count++]; + log_assert(wire); + log_assert(wire->port_output); + wire->port_output = false; + + if (flop && w->attributes.count("\\abc_flop_d")) { + RTLIL::Wire* d = outputs[outputs.size() - flopNum + flop_count]; + log_assert(d); + log_assert(d->port_output); + d->port_output = false; + } + } + if (w->port_output) { + log_assert((piNum + ci_count) < inputs.size()); + wire = inputs[piNum + ci_count++]; + log_assert(wire); + log_assert(wire->port_input); + wire->port_input = false; + + if (flop && w->attributes.count("\\abc_flop_q")) { + wire = inputs[piNum - flopNum + flop_count]; + log_assert(wire); + log_assert(wire->port_input); + wire->port_input = false; + } + } + rhs.append(wire); + } + cell->setPort(port_name, rhs); + } + + if (flop) flop_count++; + } + + dict<RTLIL::IdString, int> wideports_cache; + + if (!map_filename.empty()) { + std::ifstream mf(map_filename); + std::string type, symbol; + int variable, index; + while (mf >> type >> variable >> index >> symbol) { + RTLIL::IdString escaped_s = RTLIL::escape_id(symbol); + if (type == "input") { + log_assert(static_cast<unsigned>(variable) < inputs.size()); + RTLIL::Wire* wire = inputs[variable]; + log_assert(wire); + log_assert(wire->port_input); + + if (index == 0) { + // Cope with the fact that a CI might be identical + // to a PI (necessary due to ABC); in those cases + // simply connect the latter to the former + RTLIL::Wire* existing = module->wire(escaped_s); + if (!existing) + module->rename(wire, escaped_s); + else { + wire->port_input = false; + module->connect(wire, existing); + } + } + else if (index > 0) { + std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); + RTLIL::Wire* existing = module->wire(indexed_name); + if (!existing) { + module->rename(wire, indexed_name); + if (wideports) + wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index); + } + else { + module->connect(wire, existing); + wire->port_input = false; + } + } + } + else if (type == "output") { + log_assert(static_cast<unsigned>(variable + co_count) < outputs.size()); + RTLIL::Wire* wire = outputs[variable + co_count]; + log_assert(wire); + log_assert(wire->port_output); + if (escaped_s.in("\\__dummy_o__", "\\__const0__", "\\__const1__")) { + wire->port_output = false; + continue; + } + + if (index == 0) { + // Cope with the fact that a CO might be identical + // to a PO (necessary due to ABC); in those cases + // simply connect the latter to the former + RTLIL::Wire* existing = module->wire(escaped_s); + if (!existing) { + if (escaped_s.ends_with("$inout.out")) { + wire->port_output = false; + RTLIL::Wire *in_wire = module->wire(escaped_s.substr(0, escaped_s.size()-10)); + log_assert(in_wire); + log_assert(in_wire->port_input && !in_wire->port_output); + in_wire->port_output = true; + module->connect(in_wire, wire); + } + else + module->rename(wire, escaped_s); + } + else { + wire->port_output = false; + module->connect(wire, existing); + } + } + else if (index > 0) { + std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); + RTLIL::Wire* existing = module->wire(indexed_name); + if (!existing) { + if (escaped_s.ends_with("$inout.out")) { + wire->port_output = false; + RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(0, escaped_s.size()-10).c_str(), index)); + log_assert(in_wire); + log_assert(in_wire->port_input && !in_wire->port_output); + in_wire->port_output = true; + module->connect(in_wire, wire); + } + else { + module->rename(wire, indexed_name); + if (wideports) + wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index); + } + } + else { + module->connect(wire, existing); + wire->port_output = false; + } + } + } + else if (type == "box") { + RTLIL::Cell* cell = module->cell(stringf("$__box%d__", variable)); + if (cell) { // ABC could have optimised this box away + module->rename(cell, escaped_s); + RTLIL::Module* box_module = design->module(cell->type); + log_assert(box_module); + + for (const auto &i : cell->connections()) { + RTLIL::IdString port_name = i.first; + RTLIL::SigSpec rhs = i.second; + int index = 0; + for (auto bit : rhs.bits()) { + RTLIL::Wire* wire = bit.wire; + RTLIL::IdString escaped_s = RTLIL::escape_id(stringf("%s.%s", log_id(cell), log_id(port_name))); + if (index == 0) + module->rename(wire, escaped_s); + else if (index > 0) { + module->rename(wire, stringf("%s[%d]", escaped_s.c_str(), index)); + if (wideports) + wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index); + } + index++; + } + } + } + } + else + log_error("Symbol type '%s' not recognised.\n", type.c_str()); + } + } + + for (auto &wp : wideports_cache) { + auto name = wp.first; + int width = wp.second + 1; + + RTLIL::Wire *wire = module->wire(name); + if (wire) + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", name.c_str(), 0))); + + // Do not make ports with a mix of input/output into + // wide ports + bool port_input = false, port_output = false; + for (int i = 0; i < width; i++) { + RTLIL::IdString other_name = name.str() + stringf("[%d]", i); + RTLIL::Wire *other_wire = module->wire(other_name); + if (other_wire) { + port_input = port_input || other_wire->port_input; + port_output = port_output || other_wire->port_output; + } + } + if ((port_input && port_output) || (!port_input && !port_output)) + continue; + + wire = module->addWire(name, width); + wire->port_input = port_input; + wire->port_output = port_output; + + for (int i = 0; i < width; i++) { + RTLIL::IdString other_name = name.str() + stringf("[%d]", i); + RTLIL::Wire *other_wire = module->wire(other_name); + if (other_wire) { + other_wire->port_input = false; + other_wire->port_output = false; + if (wire->port_input) + module->connect(other_wire, SigSpec(wire, i)); + else + module->connect(SigSpec(wire, i), other_wire); + } + } + } + + module->fixup_ports(); + design->add(module); + + design->selection_stack.emplace_back(false); + RTLIL::Selection& sel = design->selection_stack.back(); + sel.select(module); + + Pass::call(design, "clean"); + + design->selection_stack.pop_back(); + + for (auto cell : module->cells().to_vector()) { + if (cell->type != "$lut") continue; + auto y_port = cell->getPort("\\Y").as_bit(); + if (y_port.wire->width == 1) + module->rename(cell, stringf("%s$lut", y_port.wire->name.c_str())); + else + module->rename(cell, stringf("%s[%d]$lut", y_port.wire->name.c_str(), y_port.offset)); + } } struct AigerFrontend : public Frontend { - AigerFrontend() : Frontend("aiger", "read AIGER file") { } - void help() YS_OVERRIDE - { - // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| - log("\n"); - log(" read_aiger [options] [filename]\n"); - log("\n"); - log("Load module from an AIGER file into the current design.\n"); - log("\n"); - log(" -module_name <module_name>\n"); - log(" Name of module to be created (default: <filename>)\n"); - log("\n"); - log(" -clk_name <wire_name>\n"); - log(" AIGER latches to be transformed into posedge DFFs clocked by wire of"); - log(" this name (default: clk)\n"); - log("\n"); - log(" -map <filename>\n"); - log(" read file with port and latch symbols\n"); - log("\n"); - log(" -wideports\n"); - log(" Merge ports that match the pattern 'name[int]' into a single\n"); - log(" multi-bit port 'name'.\n"); - log("\n"); - } - void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE - { - log_header(design, "Executing AIGER frontend.\n"); - - RTLIL::IdString clk_name = "\\clk"; - RTLIL::IdString module_name; - std::string map_filename; - bool wideports = false; - - size_t argidx; - for (argidx = 1; argidx < args.size(); argidx++) { - std::string arg = args[argidx]; - if (arg == "-module_name" && argidx+1 < args.size()) { - module_name = RTLIL::escape_id(args[++argidx]); - continue; - } - if (arg == "-clk_name" && argidx+1 < args.size()) { - clk_name = RTLIL::escape_id(args[++argidx]); - continue; - } - if (map_filename.empty() && arg == "-map" && argidx+1 < args.size()) { - map_filename = args[++argidx]; - continue; - } - if (arg == "-wideports") { - wideports = true; - continue; - } - break; - } - extra_args(f, filename, args, argidx); - - if (module_name.empty()) { + AigerFrontend() : Frontend("aiger", "read AIGER file") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" read_aiger [options] [filename]\n"); + log("\n"); + log("Load module from an AIGER file into the current design.\n"); + log("\n"); + log(" -module_name <module_name>\n"); + log(" Name of module to be created (default: <filename>)\n"); + log("\n"); + log(" -clk_name <wire_name>\n"); + log(" AIGER latches to be transformed into posedge DFFs clocked by wire of"); + log(" this name (default: clk)\n"); + log("\n"); + log(" -map <filename>\n"); + log(" read file with port and latch symbols\n"); + log("\n"); + log(" -wideports\n"); + log(" Merge ports that match the pattern 'name[int]' into a single\n"); + log(" multi-bit port 'name'.\n"); + log("\n"); + } + void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing AIGER frontend.\n"); + + RTLIL::IdString clk_name = "\\clk"; + RTLIL::IdString module_name; + std::string map_filename; + bool wideports = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-module_name" && argidx+1 < args.size()) { + module_name = RTLIL::escape_id(args[++argidx]); + continue; + } + if (arg == "-clk_name" && argidx+1 < args.size()) { + clk_name = RTLIL::escape_id(args[++argidx]); + continue; + } + if (map_filename.empty() && arg == "-map" && argidx+1 < args.size()) { + map_filename = args[++argidx]; + continue; + } + if (arg == "-wideports") { + wideports = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + if (module_name.empty()) { #ifdef _WIN32 - char fname[_MAX_FNAME]; - _splitpath(filename.c_str(), NULL /* drive */, NULL /* dir */, fname, NULL /* ext */) - module_name = fname; + char fname[_MAX_FNAME]; + _splitpath(filename.c_str(), NULL /* drive */, NULL /* dir */, fname, NULL /* ext */) + module_name = fname; #else - char* bn = strdup(filename.c_str()); - module_name = RTLIL::escape_id(bn); - free(bn); + char* bn = strdup(filename.c_str()); + module_name = RTLIL::escape_id(bn); + free(bn); #endif - } + } - AigerReader reader(design, *f, module_name, clk_name, map_filename, wideports); - reader.parse_aiger(); - } + AigerReader reader(design, *f, module_name, clk_name, map_filename, wideports); + reader.parse_aiger(); + } } AigerFrontend; YOSYS_NAMESPACE_END diff --git a/frontends/aiger/aigerparse.h b/frontends/aiger/aigerparse.h index 39757545f..7d6d70b2c 100644 --- a/frontends/aiger/aigerparse.h +++ b/frontends/aiger/aigerparse.h @@ -37,16 +37,20 @@ struct AigerReader unsigned M, I, L, O, A; unsigned B, C, J, F; // Optional in AIGER 1.9 unsigned line_count; + uint32_t piNum, flopNum; std::vector<RTLIL::Wire*> inputs; std::vector<RTLIL::Wire*> latches; std::vector<RTLIL::Wire*> outputs; + std::vector<RTLIL::Wire*> bad_properties; + std::vector<RTLIL::Cell*> boxes; AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports); void parse_aiger(); void parse_xaiger(); void parse_aiger_ascii(); void parse_aiger_binary(); + void post_process(); }; YOSYS_NAMESPACE_END diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 9f88b08c1..b5b968e9e 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -154,6 +154,7 @@ std::string AST::type2str(AstNodeType type) X(AST_GENIF) X(AST_GENCASE) X(AST_GENBLOCK) + X(AST_TECALL) X(AST_POSEDGE) X(AST_NEGEDGE) X(AST_EDGE) @@ -194,6 +195,9 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch is_logic = false; is_signed = false; is_string = false; + is_wand = false; + is_wor = false; + is_unsized = false; was_checked = false; range_valid = false; range_swapped = false; @@ -722,7 +726,7 @@ AstNode *AstNode::mkconst_int(uint32_t v, bool is_signed, int width) } // create an AST node for a constant (using a bit vector as value) -AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed) +AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed, bool is_unsized) { AstNode *node = new AstNode(AST_CONSTANT); node->is_signed = is_signed; @@ -736,9 +740,15 @@ AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signe node->range_valid = true; node->range_left = node->bits.size()-1; node->range_right = 0; + node->is_unsized = is_unsized; return node; } +AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed) +{ + return mkconst_bits(v, is_signed, false); +} + // create an AST node for a constant (using a string in bit vector form as value) AstNode *AstNode::mkconst_str(const std::vector<RTLIL::State> &v) { @@ -775,6 +785,14 @@ bool AstNode::bits_only_01() const return true; } +RTLIL::Const AstNode::bitsAsUnsizedConst(int width) +{ + RTLIL::State extbit = bits.back(); + while (width > int(bits.size())) + bits.push_back(extbit); + return RTLIL::Const(bits); +} + RTLIL::Const AstNode::bitsAsConst(int width, bool is_signed) { std::vector<RTLIL::State> bits = this->bits; @@ -951,6 +969,9 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast continue; if (child->type == AST_PARAMETER || child->type == AST_LOCALPARAM) continue; + if (child->type == AST_CELL && child->children.size() > 0 && child->children[0]->type == AST_CELLTYPE && + (child->children[0]->str == "$specify2" || child->children[0]->str == "$specify3" || child->children[0]->str == "$specrule")) + continue; blackbox_module = false; break; } @@ -1035,6 +1056,9 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast child->delete_children(); child->children.push_back(AstNode::mkconst_int(0, false, 0)); new_children.push_back(child); + } else if (child->type == AST_CELL && child->children.size() > 0 && child->children[0]->type == AST_CELLTYPE && + (child->children[0]->str == "$specify2" || child->children[0]->str == "$specify3" || child->children[0]->str == "$specrule")) { + new_children.push_back(child); } else { delete child; } diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 281cbe086..b8cde060e 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -137,7 +137,8 @@ namespace AST AST_GENIF, AST_GENCASE, AST_GENBLOCK, - + AST_TECALL, + AST_POSEDGE, AST_NEGEDGE, AST_EDGE, @@ -173,7 +174,7 @@ namespace AST // node content - most of it is unused in most node types std::string str; std::vector<RTLIL::State> bits; - bool is_input, is_output, is_reg, is_logic, is_signed, is_string, range_valid, range_swapped, was_checked; + bool is_input, is_output, is_reg, is_logic, is_signed, is_string, is_wand, is_wor, range_valid, range_swapped, was_checked, is_unsized; int port_id, range_left, range_right; uint32_t integer; double realvalue; @@ -262,6 +263,7 @@ namespace AST // helper functions for creating AST nodes for constants static AstNode *mkconst_int(uint32_t v, bool is_signed, int width = 32); + static AstNode *mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed, bool is_unsized); static AstNode *mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed); static AstNode *mkconst_str(const std::vector<RTLIL::State> &v); static AstNode *mkconst_str(const std::string &str); @@ -269,6 +271,7 @@ namespace AST // helper function for creating sign-extended const objects RTLIL::Const bitsAsConst(int width, bool is_signed); RTLIL::Const bitsAsConst(int width = -1); + RTLIL::Const bitsAsUnsizedConst(int width); RTLIL::Const asAttrConst(); RTLIL::Const asParaConst(); uint64_t asInt(bool is_signed); diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index b3a2a84be..32ed401eb 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -645,6 +645,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (!id_ast->children[0]->range_valid) log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", str.c_str()); this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1; + if (children.size() > 1) + range = children[1]; } else log_file_error(filename, linenum, "Failed to detect width for identifier %s!\n", str.c_str()); if (range) { @@ -902,7 +904,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (!range_valid) log_file_error(filename, linenum, "Signal `%s' with non-constant width!\n", str.c_str()); - log_assert(range_left >= range_right || (range_left == -1 && range_right == 0)); + if (!(range_left >= range_right || (range_left == -1 && range_right == 0))) + log_file_error(filename, linenum, "Signal `%s' with invalid width range %d!\n", str.c_str(), range_left - range_right + 1); RTLIL::Wire *wire = current_module->addWire(str, range_left - range_right + 1); wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); @@ -917,6 +920,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); wire->attributes[attr.first] = attr.second->asAttrConst(); } + + if (is_wand) wire->set_bool_attribute("\\wand"); + if (is_wor) wire->set_bool_attribute("\\wor"); } break; @@ -961,8 +967,13 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) detectSignWidth(width_hint, sign_hint); is_signed = sign_hint; - if (type == AST_CONSTANT) - return RTLIL::SigSpec(bitsAsConst()); + if (type == AST_CONSTANT) { + if (is_unsized) { + return RTLIL::SigSpec(bitsAsUnsizedConst(width_hint)); + } else { + return RTLIL::SigSpec(bitsAsConst()); + } + } RTLIL::SigSpec sig = realAsConst(width_hint); log_file_warning(filename, linenum, "converting real value %e to binary %s.\n", realvalue, log_signal(sig)); @@ -1490,10 +1501,12 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) continue; } if (child->type == AST_PARASET) { + int extra_const_flags = 0; IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str; if (child->children[0]->type == AST_REALVALUE) { log_file_warning(filename, linenum, "Replacing floating point parameter %s.%s = %f with string.\n", log_id(cell), log_id(paraname), child->children[0]->realvalue); + extra_const_flags = RTLIL::CONST_FLAG_REAL; auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue)); strnode->cloneInto(child->children[0]); delete strnode; @@ -1502,6 +1515,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) log_file_error(filename, linenum, "Parameter %s.%s with non-constant value!\n", log_id(cell), log_id(paraname)); cell->parameters[paraname] = child->children[0]->asParaConst(); + cell->parameters[paraname].flags |= extra_const_flags; continue; } if (child->type == AST_ARGUMENT) { @@ -1521,9 +1535,29 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); + log_file_error(filename, linenum, "Attribute `%s' with non-constant value.\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } + if (cell->type.in("$specify2", "$specify3")) { + int src_width = GetSize(cell->getPort("\\SRC")); + int dst_width = GetSize(cell->getPort("\\DST")); + bool full = cell->getParam("\\FULL").as_bool(); + if (!full && src_width != dst_width) + log_file_error(filename, linenum, "Parallel specify SRC width does not match DST width.\n"); + if (cell->type == "$specify3") { + int dat_width = GetSize(cell->getPort("\\DAT")); + if (dat_width != dst_width) + log_file_error(filename, linenum, "Specify DAT width does not match DST width.\n"); + } + cell->setParam("\\SRC_WIDTH", Const(src_width)); + cell->setParam("\\DST_WIDTH", Const(dst_width)); + } + if (cell->type == "$specrule") { + int src_width = GetSize(cell->getPort("\\SRC")); + int dst_width = GetSize(cell->getPort("\\DST")); + cell->setParam("\\SRC_WIDTH", Const(src_width)); + cell->setParam("\\DST_WIDTH", Const(dst_width)); + } } break; @@ -1541,6 +1575,37 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) delete always; } break; + case AST_TECALL: { + int sz = children.size(); + if (str == "$info") { + if (sz > 0) + log_file_info(filename, linenum, "%s.\n", children[0]->str.c_str()); + else + log_file_info(filename, linenum, "\n"); + } else if (str == "$warning") { + if (sz > 0) + log_file_warning(filename, linenum, "%s.\n", children[0]->str.c_str()); + else + log_file_warning(filename, linenum, "\n"); + } else if (str == "$error") { + if (sz > 0) + log_file_error(filename, linenum, "%s.\n", children[0]->str.c_str()); + else + log_file_error(filename, linenum, "\n"); + } else if (str == "$fatal") { + // TODO: 1st parameter, if exists, is 0,1 or 2, and passed to $finish() + // if no parameter is given, default value is 1 + // dollar_finish(sz ? children[0] : 1); + // perhaps create & use log_file_fatal() + if (sz > 0) + log_file_error(filename, linenum, "FATAL: %s.\n", children[0]->str.c_str()); + else + log_file_error(filename, linenum, "FATAL.\n"); + } else { + log_file_error(filename, linenum, "Unknown elabortoon system task '%s'.\n", str.c_str()); + } + } break; + case AST_FCALL: { if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") { diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 3e453bd7f..e947125bf 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -1172,6 +1172,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, varbuf->children[0] = buf; } + if (type == AST_FOR) { + AstNode *buf = next_ast->clone(); + delete buf->children[1]; + buf->children[1] = varbuf->children[0]->clone(); + current_block->children.insert(current_block->children.begin() + current_block_idx++, buf); + } + current_scope[varbuf->str] = backup_scope_varbuf; delete varbuf; delete_children(); @@ -1598,6 +1605,7 @@ skip_dynamic_range_lvalue_expansion:; current_scope[wire_tmp->str] = wire_tmp; wire_tmp->attributes["\\nosync"] = AstNode::mkconst_int(1, false); while (wire_tmp->simplify(true, false, false, 1, -1, false, false)) { } + wire_tmp->is_logic = true; AstNode *wire_tmp_id = new AstNode(AST_IDENTIFIER); wire_tmp_id->str = wire_tmp->str; diff --git a/frontends/ilang/ilang_lexer.l b/frontends/ilang/ilang_lexer.l index d8e01ae4d..4fd0ae855 100644 --- a/frontends/ilang/ilang_lexer.l +++ b/frontends/ilang/ilang_lexer.l @@ -53,6 +53,7 @@ USING_YOSYS_NAMESPACE "attribute" { return TOK_ATTRIBUTE; } "parameter" { return TOK_PARAMETER; } "signed" { return TOK_SIGNED; } +"real" { return TOK_REAL; } "wire" { return TOK_WIRE; } "memory" { return TOK_MEMORY; } "width" { return TOK_WIDTH; } diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y index f83824088..44c99906a 100644 --- a/frontends/ilang/ilang_parser.y +++ b/frontends/ilang/ilang_parser.y @@ -45,7 +45,16 @@ YOSYS_NAMESPACE_END USING_YOSYS_NAMESPACE %} -%name-prefix "rtlil_frontend_ilang_yy" +%define api.prefix {rtlil_frontend_ilang_yy} + +/* The union is defined in the header, so we need to provide all the + * includes it requires + */ +%code requires { +#include <string> +#include <vector> +#include "frontends/ilang/ilang_frontend.h" +} %union { char *string; @@ -61,7 +70,7 @@ USING_YOSYS_NAMESPACE %token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC %token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS TOK_GLOBAL TOK_INIT %token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET -%token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_UPTO +%token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_REAL TOK_UPTO %type <rsigspec> sigspec_list_reversed %type <sigspec> sigspec sigspec_list @@ -241,6 +250,12 @@ cell_body: free($4); delete $5; } | + cell_body TOK_PARAMETER TOK_REAL TOK_ID constant EOL { + current_cell->parameters[$4] = *$5; + current_cell->parameters[$4].flags |= RTLIL::CONST_FLAG_REAL; + free($4); + delete $5; + } | cell_body TOK_CONNECT TOK_ID sigspec EOL { if (current_cell->hasPort($3)) rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of cell port %s.", $3).c_str()); @@ -445,4 +460,3 @@ conn_stmt: delete $2; delete $3; }; - diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index ed9727b88..2bf99e58e 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -46,7 +46,15 @@ USING_YOSYS_NAMESPACE #include "VeriModule.h" #include "VeriWrite.h" #include "VhdlUnits.h" -#include "Message.h" +#include "VeriLibrary.h" + +#ifndef SYMBIOTIC_VERIFIC_API_VERSION +# error "Only Symbiotic EDA flavored Verific is supported. Please contact office@symbioticeda.com for commercial support for Yosys+Verific." +#endif + +#if SYMBIOTIC_VERIFIC_API_VERSION < 1 +# error "Please update your version of Symbiotic EDA flavored Verific." +#endif #ifdef __clang__ #pragma clang diagnostic pop @@ -776,13 +784,14 @@ void VerificImporter::merge_past_ffs(pool<RTLIL::Cell*> &candidates) void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo) { - std::string module_name = nl->IsOperator() ? std::string("$verific$") + nl->Owner()->Name() : RTLIL::escape_id(nl->Owner()->Name()); + std::string netlist_name = nl->GetAtt(" \\top") ? nl->CellBaseName() : nl->Owner()->Name(); + std::string module_name = nl->IsOperator() ? "$verific$" + netlist_name : RTLIL::escape_id(netlist_name); netlist = nl; if (design->has(module_name)) { if (!nl->IsOperator() && !is_blackbox(nl)) - log_cmd_error("Re-definition of module `%s'.\n", nl->Owner()->Name()); + log_cmd_error("Re-definition of module `%s'.\n", netlist_name.c_str()); return; } @@ -1752,32 +1761,64 @@ struct VerificExtNets } }; -void verific_import(Design *design, std::string top) +void verific_import(Design *design, const std::map<std::string,std::string> ¶meters, std::string top) { verific_sva_fsm_limit = 16; std::set<Netlist*> nl_todo, nl_done; - { - VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1); - VeriLibrary *veri_lib = veri_file::GetLibrary("work", 1); + VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1); + VeriLibrary *veri_lib = veri_file::GetLibrary("work", 1); + Array *netlists = NULL; + Array veri_libs, vhdl_libs; + if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib); + if (veri_lib) veri_libs.InsertLast(veri_lib); - Array veri_libs, vhdl_libs; - if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib); - if (veri_lib) veri_libs.InsertLast(veri_lib); + Map verific_params(STRING_HASH); + for (const auto &i : parameters) + verific_params.Insert(i.first.c_str(), i.second.c_str()); - Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs); - Netlist *nl; - int i; + if (top.empty()) { + netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, &verific_params); + } + else { + Array veri_modules, vhdl_units; + + if (veri_lib) { + VeriModule *veri_module = veri_lib->GetModule(top.c_str(), 1); + if (veri_module) { + veri_modules.InsertLast(veri_module); + } + + // Also elaborate all root modules since they may contain bind statements + MapIter mi; + FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) { + if (!veri_module->IsRootModule()) continue; + veri_modules.InsertLast(veri_module); + } + } - FOREACH_ARRAY_ITEM(netlists, i, nl) { - if (top.empty() || nl->Owner()->Name() == top) - nl_todo.insert(nl); + if (vhdl_lib) { + VhdlDesignUnit *vhdl_unit = vhdl_lib->GetPrimUnit(top.c_str()); + if (vhdl_unit) + vhdl_units.InsertLast(vhdl_unit); } - delete netlists; + netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, &verific_params); } + Netlist *nl; + int i; + + FOREACH_ARRAY_ITEM(netlists, i, nl) { + if (top.empty() && nl->CellBaseName() != top) + continue; + nl->AddAtt(new Att(" \\top", NULL)); + nl_todo.insert(nl); + } + + delete netlists; + if (!verific_error_msg.empty()) log_error("%s\n", verific_error_msg.c_str()); @@ -1983,6 +2024,9 @@ struct VerificPass : public Pass { // WARNING: instantiating unknown module 'XYZ' (VERI-1063) Message::SetMessageType("VERI-1063", VERIFIC_ERROR); + // https://github.com/YosysHQ/yosys/issues/1055 + RuntimeFlags::SetVar("veri_elaborate_top_level_modules_having_interface_ports", 1) ; + #ifndef DB_PRESERVE_INITIAL_VALUE # warning Verific was built without DB_PRESERVE_INITIAL_VALUE. #endif @@ -2212,8 +2256,8 @@ struct VerificPass : public Pass { continue; } if (args[argidx] == "-chparam" && argidx+2 < GetSize(args)) { - const std::string &key = args[++argidx]; - const std::string &value = args[++argidx]; + const std::string &key = args[++argidx]; + const std::string &value = args[++argidx]; unsigned new_insertion = parameters.Insert(key.c_str(), value.c_str(), 1 /* force_overwrite */); if (!new_insertion) @@ -2270,12 +2314,22 @@ struct VerificPass : public Pass { for (; argidx < GetSize(args); argidx++) { const char *name = args[argidx].c_str(); + VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1); + + if (veri_lib) { + VeriModule *veri_module = veri_lib->GetModule(name, 1); + if (veri_module) { + log("Adding Verilog module '%s' to elaboration queue.\n", name); + veri_modules.InsertLast(veri_module); + continue; + } - VeriModule *veri_module = veri_file::GetModule(name); - if (veri_module) { - log("Adding Verilog module '%s' to elaboration queue.\n", name); - veri_modules.InsertLast(veri_module); - continue; + // Also elaborate all root modules since they may contain bind statements + MapIter mi; + FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) { + if (!veri_module->IsRootModule()) continue; + veri_modules.InsertLast(veri_module); + } } VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1); @@ -2294,8 +2348,10 @@ struct VerificPass : public Pass { Netlist *nl; int i; - FOREACH_ARRAY_ITEM(netlists, i, nl) + FOREACH_ARRAY_ITEM(netlists, i, nl) { + nl->AddAtt(new Att(" \\top", NULL)); nl_todo.insert(nl); + } delete netlists; } diff --git a/frontends/verific/verific.h b/frontends/verific/verific.h index b331dd4b9..88a6cc0ba 100644 --- a/frontends/verific/verific.h +++ b/frontends/verific/verific.h @@ -26,7 +26,7 @@ YOSYS_NAMESPACE_BEGIN extern int verific_verbose; extern bool verific_import_pending; -extern void verific_import(Design *design, std::string top = std::string()); +extern void verific_import(Design *design, const std::map<std::string,std::string> ¶meters, std::string top = std::string()); extern pool<int> verific_sva_prims; diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc index 0a1f97ac0..6a8462b41 100644 --- a/frontends/verilog/Makefile.inc +++ b/frontends/verilog/Makefile.inc @@ -14,7 +14,7 @@ frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l $(Q) mkdir -p $(dir $@) $(P) flex -o frontends/verilog/verilog_lexer.cc $< -frontends/verilog/verilog_parser.tab.o: CXXFLAGS += -DYYMAXDEPTH=100000 +frontends/verilog/verilog_parser.tab.o: CXXFLAGS += -DYYMAXDEPTH=10000000 OBJS += frontends/verilog/verilog_parser.tab.o OBJS += frontends/verilog/verilog_lexer.o diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc index 7848c626d..57d366dbf 100644 --- a/frontends/verilog/const2ast.cc +++ b/frontends/verilog/const2ast.cc @@ -71,7 +71,7 @@ static int my_ilog2(int x) } // parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?') -static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type) +static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type, bool is_unsized) { // all digits in string (MSB at index 0) std::vector<uint8_t> digits; @@ -129,6 +129,9 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le return; } + if (is_unsized && (len > len_in_bits)) + log_file_error(current_filename, get_line_num(), "Unsized constant must have width of 1 bit, but have %d bits!\n", len); + for (len = len - 1; len >= 0; len--) if (data[len] == RTLIL::S1) break; @@ -186,7 +189,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn // Simple base-10 integer if (*endptr == 0) { std::vector<RTLIL::State> data; - my_strtobin(data, str, -1, 10, case_type); + my_strtobin(data, str, -1, 10, case_type, false); if (data.back() == RTLIL::S1) data.push_back(RTLIL::S0); return AstNode::mkconst_bits(data, true); @@ -201,6 +204,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn { std::vector<RTLIL::State> data; bool is_signed = false; + bool is_unsized = false; if (*(endptr+1) == 's') { is_signed = true; endptr++; @@ -209,28 +213,34 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn { case 'b': case 'B': - my_strtobin(data, endptr+2, len_in_bits, 2, case_type); + my_strtobin(data, endptr+2, len_in_bits, 2, case_type, false); break; case 'o': case 'O': - my_strtobin(data, endptr+2, len_in_bits, 8, case_type); + my_strtobin(data, endptr+2, len_in_bits, 8, case_type, false); break; case 'd': case 'D': - my_strtobin(data, endptr+2, len_in_bits, 10, case_type); + my_strtobin(data, endptr+2, len_in_bits, 10, case_type, false); break; case 'h': case 'H': - my_strtobin(data, endptr+2, len_in_bits, 16, case_type); + my_strtobin(data, endptr+2, len_in_bits, 16, case_type, false); break; default: - return NULL; + char next_char = char(tolower(*(endptr+1))); + if (next_char == '0' || next_char == '1' || next_char == 'x' || next_char == 'z') { + my_strtobin(data, endptr+1, 1, 2, case_type, true); + is_unsized = true; + } else { + return NULL; + } } if (len_in_bits < 0) { if (is_signed && data.back() == RTLIL::S1) data.push_back(RTLIL::S0); } - return AstNode::mkconst_bits(data, is_signed); + return AstNode::mkconst_bits(data, is_signed, is_unsized); } return NULL; diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index ed6ce2ecb..01e589efb 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -158,6 +158,9 @@ struct VerilogFrontend : public Frontend { log(" delete (* whitebox *) and (* lib_whitebox *) attributes from\n"); log(" all modules.\n"); log("\n"); + log(" -specify\n"); + log(" parse and import specify blocks\n"); + log("\n"); log(" -noopt\n"); log(" don't perform basic optimizations (such as const folding) in the\n"); log(" high-level front-end.\n"); @@ -228,6 +231,8 @@ struct VerilogFrontend : public Frontend { bool flag_nooverwrite = false; bool flag_overwrite = false; bool flag_defer = false; + bool flag_noblackbox = false; + bool flag_nowb = false; std::map<std::string, std::string> defines_map; std::list<std::string> include_dirs; std::list<std::string> attributes; @@ -237,13 +242,10 @@ struct VerilogFrontend : public Frontend { formal_mode = false; norestrict_mode = false; assume_asserts_mode = false; - noblackbox_mode = false; lib_mode = false; - nowb_mode = false; + specify_mode = false; default_nettype_wire = true; - log_header(design, "Executing Verilog-2005 frontend.\n"); - args.insert(args.begin()+1, verilog_defaults.begin(), verilog_defaults.end()); size_t argidx; @@ -342,7 +344,7 @@ struct VerilogFrontend : public Frontend { continue; } if (arg == "-noblackbox") { - noblackbox_mode = true; + flag_noblackbox = true; continue; } if (arg == "-lib") { @@ -351,7 +353,11 @@ struct VerilogFrontend : public Frontend { continue; } if (arg == "-nowb") { - nowb_mode = true; + flag_nowb = true; + continue; + } + if (arg == "-specify") { + specify_mode = true; continue; } if (arg == "-noopt") { @@ -415,6 +421,8 @@ struct VerilogFrontend : public Frontend { } extra_args(f, filename, args, argidx); + log_header(design, "Executing Verilog-2005 frontend: %s\n", filename.c_str()); + log("Parsing %s%s input from `%s' to AST representation.\n", formal_mode ? "formal " : "", sv_mode ? "SystemVerilog" : "Verilog", filename.c_str()); @@ -450,7 +458,7 @@ struct VerilogFrontend : public Frontend { error_on_dpi_function(current_ast); AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, - flag_nomeminit, flag_nomem2reg, flag_mem2reg, noblackbox_mode, lib_mode, nowb_mode, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); + flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, lib_mode, flag_nowb, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); if (!flag_nopp) delete lexin; diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h index ca40946cb..a7c9b2fe6 100644 --- a/frontends/verilog/verilog_frontend.h +++ b/frontends/verilog/verilog_frontend.h @@ -69,14 +69,11 @@ namespace VERILOG_FRONTEND // running in -assert-assumes mode extern bool assert_assumes_mode; - // running in -noblackbox mode - extern bool noblackbox_mode; - // running in -lib mode extern bool lib_mode; - // running in -nowb mode - extern bool nowb_mode; + // running in -specify mode + extern bool specify_mode; // lexer input stream extern std::istream *lexin; diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index 6ef38252a..3c612472d 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -148,7 +148,7 @@ YOSYS_NAMESPACE_END "endfunction" { return TOK_ENDFUNCTION; } "task" { return TOK_TASK; } "endtask" { return TOK_ENDTASK; } -"specify" { return TOK_SPECIFY; } +"specify" { return specify_mode ? TOK_SPECIFY : TOK_IGNORED_SPECIFY; } "endspecify" { return TOK_ENDSPECIFY; } "specparam" { return TOK_SPECPARAM; } "package" { SV_KEYWORD(TOK_PACKAGE); } @@ -206,7 +206,9 @@ YOSYS_NAMESPACE_END "const" { if (formal_mode) return TOK_CONST; SV_KEYWORD(TOK_CONST); } "checker" { if (formal_mode) return TOK_CHECKER; SV_KEYWORD(TOK_CHECKER); } "endchecker" { if (formal_mode) return TOK_ENDCHECKER; SV_KEYWORD(TOK_ENDCHECKER); } +"final" { SV_KEYWORD(TOK_FINAL); } "logic" { SV_KEYWORD(TOK_LOGIC); } +"var" { SV_KEYWORD(TOK_VAR); } "bit" { SV_KEYWORD(TOK_REG); } "eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } @@ -216,6 +218,8 @@ YOSYS_NAMESPACE_END "output" { return TOK_OUTPUT; } "inout" { return TOK_INOUT; } "wire" { return TOK_WIRE; } +"wor" { return TOK_WOR; } +"wand" { return TOK_WAND; } "reg" { return TOK_REG; } "integer" { return TOK_INTEGER; } "signed" { return TOK_SIGNED; } @@ -230,7 +234,7 @@ YOSYS_NAMESPACE_END return TOK_CONSTVAL; } -[0-9]*[ \t]*\'s?[bodhBODH][ \t\r\n]*[0-9a-fA-FzxZX?_]+ { +[0-9]*[ \t]*\'s?[bodhBODH]*[ \t\r\n]*[0-9a-fA-FzxZX?_]+ { frontend_verilog_yylval.string = new std::string(yytext); return TOK_CONSTVAL; } @@ -301,6 +305,17 @@ supply1 { return TOK_SUPPLY1; } return TOK_ID; } +"$"(setup|hold|setuphold|removal|recovery|recrem|skew|timeskew|fullskew|nochange) { + if (!specify_mode) REJECT; + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_ID; +} + +"$"(info|warning|error|fatal) { + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_ELAB_TASK; +} + "$signed" { return TOK_TO_SIGNED; } "$unsigned" { return TOK_TO_UNSIGNED; } @@ -411,6 +426,17 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { "+:" { return TOK_POS_INDEXED; } "-:" { return TOK_NEG_INDEXED; } +[-+]?[=*]> { + if (!specify_mode) REJECT; + frontend_verilog_yylval.string = new std::string(yytext); + return TOK_SPECIFY_OPER; +} + +"&&&" { + if (!specify_mode) REJECT; + return TOK_SPECIFY_AND; +} + "/*" { BEGIN(COMMENT); } <COMMENT>. /* ignore comment body */ <COMMENT>\n /* ignore comment body */ diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 40968d17a..a034f9601 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -59,7 +59,7 @@ namespace VERILOG_FRONTEND { std::vector<char> case_type_stack; bool do_not_require_port_stubs; bool default_nettype_wire; - bool sv_mode, formal_mode, noblackbox_mode, lib_mode, nowb_mode; + bool sv_mode, formal_mode, lib_mode, specify_mode; bool noassert_mode, noassume_mode, norestrict_mode; bool assume_asserts_mode, assert_assumes_mode; bool current_wire_rand, current_wire_const; @@ -94,29 +94,58 @@ static void free_attr(std::map<std::string, AstNode*> *al) delete al; } +struct specify_target { + char polarity_op; + AstNode *dst, *dat; +}; + +struct specify_triple { + AstNode *t_min, *t_avg, *t_max; +}; + +struct specify_rise_fall { + specify_triple rise; + specify_triple fall; +}; + %} -%name-prefix "frontend_verilog_yy" +%define api.prefix {frontend_verilog_yy} + +/* The union is defined in the header, so we need to provide all the + * includes it requires + */ +%code requires { +#include <map> +#include <string> +#include "frontends/verilog/verilog_frontend.h" +} %union { std::string *string; struct YOSYS_NAMESPACE_PREFIX AST::AstNode *ast; std::map<std::string, YOSYS_NAMESPACE_PREFIX AST::AstNode*> *al; + struct specify_target *specify_target_ptr; + struct specify_triple *specify_triple_ptr; + struct specify_rise_fall *specify_rise_fall_ptr; bool boolean; + char ch; } -%token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE TOK_SVA_LABEL -%token TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER +%token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE +%token <string> TOK_SVA_LABEL TOK_SPECIFY_OPER TOK_ELAB_TASK +%token TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER TOK_FINAL %token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END %token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM %token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP -%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT -%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG TOK_LOGIC +%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR +%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_WAND TOK_WOR TOK_REG TOK_LOGIC %token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL %token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT %token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR TOK_AUTOMATIC %token TOK_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT -%token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK TOK_SPECIFY TOK_ENDSPECIFY TOK_SPECPARAM +%token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK TOK_SPECIFY +%token TOK_IGNORED_SPECIFY TOK_ENDSPECIFY TOK_SPECPARAM TOK_SPECIFY_AND %token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR TOK_REAL %token TOK_SYNOPSYS_FULL_CASE TOK_SYNOPSYS_PARALLEL_CASE %token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED @@ -130,6 +159,12 @@ static void free_attr(std::map<std::string, AstNode*> *al) %type <boolean> opt_signed opt_property unique_case_attr %type <al> attr case_attr +%type <specify_target_ptr> specify_target +%type <specify_triple_ptr> specify_triple +%type <specify_rise_fall_ptr> specify_rise_fall +%type <ast> specify_if specify_condition specify_opt_arg +%type <ch> specify_edge + // operator precedence from low to high %left OP_LOR %left OP_LAND @@ -450,12 +485,21 @@ wire_type_token_io: wire_type_token: TOK_WIRE { } | + TOK_WOR { + astbuf3->is_wor = true; + } | + TOK_WAND { + astbuf3->is_wand = true; + } | TOK_REG { astbuf3->is_reg = true; } | TOK_LOGIC { astbuf3->is_logic = true; } | + TOK_VAR { + astbuf3->is_logic = true; + } | TOK_INTEGER { astbuf3->is_reg = true; astbuf3->range_left = 31; @@ -539,7 +583,7 @@ module_body: module_body_stmt: task_func_decl | specify_block |param_decl | localparam_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt | - always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl; + always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block; checker_decl: TOK_CHECKER TOK_ID ';' { @@ -697,15 +741,254 @@ task_func_body: task_func_body behavioral_stmt | /* empty */; +/*************************** specify parser ***************************/ + specify_block: - TOK_SPECIFY specify_item_opt TOK_ENDSPECIFY | - TOK_SPECIFY TOK_ENDSPECIFY ; + TOK_SPECIFY specify_item_list TOK_ENDSPECIFY; -specify_item_opt: - specify_item_opt specify_item | - specify_item ; +specify_item_list: + specify_item specify_item_list | + /* empty */; specify_item: + specify_if '(' specify_edge expr TOK_SPECIFY_OPER specify_target ')' '=' specify_rise_fall ';' { + AstNode *en_expr = $1; + char specify_edge = $3; + AstNode *src_expr = $4; + string *oper = $5; + specify_target *target = $6; + specify_rise_fall *timing = $9; + + if (specify_edge != 0 && target->dat == nullptr) + frontend_verilog_yyerror("Found specify edge but no data spec.\n"); + + AstNode *cell = new AstNode(AST_CELL); + ast_stack.back()->children.push_back(cell); + cell->str = stringf("$specify$%d", autoidx++); + cell->children.push_back(new AstNode(AST_CELLTYPE)); + cell->children.back()->str = target->dat ? "$specify3" : "$specify2"; + + char oper_polarity = 0; + char oper_type = oper->at(0); + + if (oper->size() == 3) { + oper_polarity = oper->at(0); + oper_type = oper->at(1); + } + + cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_type == '*', false, 1))); + cell->children.back()->str = "\\FULL"; + + cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_polarity != 0, false, 1))); + cell->children.back()->str = "\\SRC_DST_PEN"; + + cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_polarity == '+', false, 1))); + cell->children.back()->str = "\\SRC_DST_POL"; + + cell->children.push_back(new AstNode(AST_PARASET, timing->rise.t_min)); + cell->children.back()->str = "\\T_RISE_MIN"; + + cell->children.push_back(new AstNode(AST_PARASET, timing->rise.t_avg)); + cell->children.back()->str = "\\T_RISE_TYP"; + + cell->children.push_back(new AstNode(AST_PARASET, timing->rise.t_max)); + cell->children.back()->str = "\\T_RISE_MAX"; + + cell->children.push_back(new AstNode(AST_PARASET, timing->fall.t_min)); + cell->children.back()->str = "\\T_FALL_MIN"; + + cell->children.push_back(new AstNode(AST_PARASET, timing->fall.t_avg)); + cell->children.back()->str = "\\T_FALL_TYP"; + + cell->children.push_back(new AstNode(AST_PARASET, timing->fall.t_max)); + cell->children.back()->str = "\\T_FALL_MAX"; + + cell->children.push_back(new AstNode(AST_ARGUMENT, en_expr ? en_expr : AstNode::mkconst_int(1, false, 1))); + cell->children.back()->str = "\\EN"; + + cell->children.push_back(new AstNode(AST_ARGUMENT, src_expr)); + cell->children.back()->str = "\\SRC"; + + cell->children.push_back(new AstNode(AST_ARGUMENT, target->dst)); + cell->children.back()->str = "\\DST"; + + if (target->dat) + { + cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(specify_edge != 0, false, 1))); + cell->children.back()->str = "\\EDGE_EN"; + + cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(specify_edge == 'p', false, 1))); + cell->children.back()->str = "\\EDGE_POL"; + + cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(target->polarity_op != 0, false, 1))); + cell->children.back()->str = "\\DAT_DST_PEN"; + + cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(target->polarity_op == '+', false, 1))); + cell->children.back()->str = "\\DAT_DST_POL"; + + cell->children.push_back(new AstNode(AST_ARGUMENT, target->dat)); + cell->children.back()->str = "\\DAT"; + } + + delete oper; + delete target; + delete timing; + } | + TOK_ID '(' specify_edge expr specify_condition ',' specify_edge expr specify_condition ',' expr specify_opt_arg ')' ';' { + if (*$1 != "$setup" && *$1 != "$hold" && *$1 != "$setuphold" && *$1 != "$removal" && *$1 != "$recovery" && + *$1 != "$recrem" && *$1 != "$skew" && *$1 != "$timeskew" && *$1 != "$fullskew" && *$1 != "$nochange") + frontend_verilog_yyerror("Unsupported specify rule type: %s\n", $1->c_str()); + + AstNode *src_pen = AstNode::mkconst_int($3 != 0, false, 1); + AstNode *src_pol = AstNode::mkconst_int($3 == 'p', false, 1); + AstNode *src_expr = $4, *src_en = $5 ? $5 : AstNode::mkconst_int(1, false, 1); + + AstNode *dst_pen = AstNode::mkconst_int($7 != 0, false, 1); + AstNode *dst_pol = AstNode::mkconst_int($7 == 'p', false, 1); + AstNode *dst_expr = $8, *dst_en = $9 ? $9 : AstNode::mkconst_int(1, false, 1); + + AstNode *limit = $11; + AstNode *limit2 = $12; + + AstNode *cell = new AstNode(AST_CELL); + ast_stack.back()->children.push_back(cell); + cell->str = stringf("$specify$%d", autoidx++); + cell->children.push_back(new AstNode(AST_CELLTYPE)); + cell->children.back()->str = "$specrule"; + + cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_str(*$1))); + cell->children.back()->str = "\\TYPE"; + + cell->children.push_back(new AstNode(AST_PARASET, limit)); + cell->children.back()->str = "\\T_LIMIT"; + + cell->children.push_back(new AstNode(AST_PARASET, limit2 ? limit2 : AstNode::mkconst_int(0, true))); + cell->children.back()->str = "\\T_LIMIT2"; + + cell->children.push_back(new AstNode(AST_PARASET, src_pen)); + cell->children.back()->str = "\\SRC_PEN"; + + cell->children.push_back(new AstNode(AST_PARASET, src_pol)); + cell->children.back()->str = "\\SRC_POL"; + + cell->children.push_back(new AstNode(AST_PARASET, dst_pen)); + cell->children.back()->str = "\\DST_PEN"; + + cell->children.push_back(new AstNode(AST_PARASET, dst_pol)); + cell->children.back()->str = "\\DST_POL"; + + cell->children.push_back(new AstNode(AST_ARGUMENT, src_en)); + cell->children.back()->str = "\\SRC_EN"; + + cell->children.push_back(new AstNode(AST_ARGUMENT, src_expr)); + cell->children.back()->str = "\\SRC"; + + cell->children.push_back(new AstNode(AST_ARGUMENT, dst_en)); + cell->children.back()->str = "\\DST_EN"; + + cell->children.push_back(new AstNode(AST_ARGUMENT, dst_expr)); + cell->children.back()->str = "\\DST"; + + delete $1; + }; + +specify_opt_arg: + ',' expr { + $$ = $2; + } | + /* empty */ { + $$ = nullptr; + }; + +specify_if: + TOK_IF '(' expr ')' { + $$ = $3; + } | + /* empty */ { + $$ = nullptr; + }; + +specify_condition: + TOK_SPECIFY_AND expr { + $$ = $2; + } | + /* empty */ { + $$ = nullptr; + }; + +specify_target: + expr { + $$ = new specify_target; + $$->polarity_op = 0; + $$->dst = $1; + $$->dat = nullptr; + } | + '(' expr ':' expr ')'{ + $$ = new specify_target; + $$->polarity_op = 0; + $$->dst = $2; + $$->dat = $4; + } | + '(' expr TOK_NEG_INDEXED expr ')'{ + $$ = new specify_target; + $$->polarity_op = '-'; + $$->dst = $2; + $$->dat = $4; + } | + '(' expr TOK_POS_INDEXED expr ')'{ + $$ = new specify_target; + $$->polarity_op = '+'; + $$->dst = $2; + $$->dat = $4; + }; + +specify_edge: + TOK_POSEDGE { $$ = 'p'; } | + TOK_NEGEDGE { $$ = 'n'; } | + { $$ = 0; }; + +specify_rise_fall: + specify_triple { + $$ = new specify_rise_fall; + $$->rise = *$1; + $$->fall.t_min = $1->t_min->clone(); + $$->fall.t_avg = $1->t_avg->clone(); + $$->fall.t_max = $1->t_max->clone(); + delete $1; + } | + '(' specify_triple ',' specify_triple ')' { + $$ = new specify_rise_fall; + $$->rise = *$2; + $$->fall = *$4; + delete $2; + delete $4; + }; + +specify_triple: + expr { + $$ = new specify_triple; + $$->t_min = $1; + $$->t_avg = $1->clone(); + $$->t_max = $1->clone(); + } | + expr ':' expr ':' expr { + $$ = new specify_triple; + $$->t_min = $1; + $$->t_avg = $3; + $$->t_max = $5; + }; + +/******************** ignored specify parser **************************/ + +ignored_specify_block: + TOK_IGNORED_SPECIFY ignored_specify_item_opt TOK_ENDSPECIFY | + TOK_IGNORED_SPECIFY TOK_ENDSPECIFY ; + +ignored_specify_item_opt: + ignored_specify_item_opt ignored_specify_item | + ignored_specify_item ; + +ignored_specify_item: specparam_declaration // | pulsestyle_declaration // | showcancelled_declaration @@ -721,13 +1004,13 @@ specparam_declaration: // and the 'non_opt_range' rule allows index ranges not allowed by 1364-2005 // exxxxtending this for SV specparam would change this anyhow specparam_range: - '[' constant_expression ':' constant_expression ']' ; + '[' ignspec_constant_expression ':' ignspec_constant_expression ']' ; list_of_specparam_assignments: specparam_assignment | list_of_specparam_assignments ',' specparam_assignment; specparam_assignment: - TOK_ID '=' constant_mintypmax_expression ; + ignspec_id '=' constant_mintypmax_expression ; /* pulsestyle_declaration : @@ -802,19 +1085,19 @@ opt_polarity_operator : // Good enough for the time being specify_input_terminal_descriptor : - TOK_ID ; + ignspec_id ; // Good enough for the time being specify_output_terminal_descriptor : - TOK_ID ; + ignspec_id ; system_timing_declaration : - TOK_ID '(' system_timing_args ')' ';' ; + ignspec_id '(' system_timing_args ')' ';' ; system_timing_arg : - TOK_POSEDGE TOK_ID | - TOK_NEGEDGE TOK_ID | - expr ; + TOK_POSEDGE ignspec_id | + TOK_NEGEDGE ignspec_id | + ignspec_expr ; system_timing_args : system_timing_arg | @@ -871,19 +1154,27 @@ tzx_path_delay_expression : */ path_delay_expression : - constant_expression; + ignspec_constant_expression; constant_mintypmax_expression : - constant_expression - | constant_expression ':' constant_expression ':' constant_expression + ignspec_constant_expression + | ignspec_constant_expression ':' ignspec_constant_expression ':' ignspec_constant_expression ; // for the time being this is OK, but we may write our own expr here. // as I'm not sure it is legal to use a full expr here (probably not) // On the other hand, other rules requiring constant expressions also use 'expr' // (such as param assignment), so we may leave this as-is, perhaps adding runtime checks for constant-ness -constant_expression: - expr ; +ignspec_constant_expression: + expr { delete $1; }; + +ignspec_expr: + expr { delete $1; }; + +ignspec_id: + TOK_ID { delete $1; }; + +/**********************************************************************/ param_signed: TOK_SIGNED { @@ -917,7 +1208,7 @@ param_range: }; param_decl: - TOK_PARAMETER { + attr TOK_PARAMETER { astbuf1 = new AstNode(AST_PARAMETER); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); } param_signed param_integer param_real param_range param_decl_list ';' { @@ -925,7 +1216,7 @@ param_decl: }; localparam_decl: - TOK_LOCALPARAM { + attr TOK_LOCALPARAM { astbuf1 = new AstNode(AST_LOCALPARAM); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); } param_signed param_integer param_real param_range param_decl_list ';' { @@ -1241,27 +1532,40 @@ cell_port_list_rules: cell_port | cell_port_list_rules ',' cell_port; cell_port: - /* empty */ { + attr { AstNode *node = new AstNode(AST_ARGUMENT); astbuf2->children.push_back(node); + free_attr($1); } | - expr { + attr expr { AstNode *node = new AstNode(AST_ARGUMENT); astbuf2->children.push_back(node); - node->children.push_back($1); + node->children.push_back($2); + free_attr($1); } | - '.' TOK_ID '(' expr ')' { + attr '.' TOK_ID '(' expr ')' { AstNode *node = new AstNode(AST_ARGUMENT); - node->str = *$2; + node->str = *$3; astbuf2->children.push_back(node); - node->children.push_back($4); - delete $2; + node->children.push_back($5); + delete $3; + free_attr($1); } | - '.' TOK_ID '(' ')' { + attr '.' TOK_ID '(' ')' { AstNode *node = new AstNode(AST_ARGUMENT); - node->str = *$2; + node->str = *$3; astbuf2->children.push_back(node); - delete $2; + delete $3; + free_attr($1); + } | + attr '.' TOK_ID { + AstNode *node = new AstNode(AST_ARGUMENT); + node->str = *$3; + astbuf2->children.push_back(node); + node->children.push_back(new AstNode(AST_IDENTIFIER)); + node->children.back()->str = *$3; + delete $3; + free_attr($1); }; always_stmt: @@ -1341,6 +1645,9 @@ opt_property: TOK_PROPERTY { $$ = true; } | + TOK_FINAL { + $$ = false; + } | /* empty */ { $$ = false; }; @@ -1869,6 +2176,15 @@ gen_stmt: if ($6 != NULL) delete $6; ast_stack.pop_back(); + } | + TOK_ELAB_TASK { + AstNode *node = new AstNode(AST_TECALL); + node->str = *$1; + delete $1; + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + } opt_arg_list ';'{ + ast_stack.pop_back(); }; gen_stmt_block: @@ -2139,4 +2455,3 @@ concat_list: $$ = $3; $$->children.push_back($1); }; - diff --git a/kernel/cellaigs.cc b/kernel/cellaigs.cc index 5fd76afe5..26c625f89 100644 --- a/kernel/cellaigs.cc +++ b/kernel/cellaigs.cc @@ -453,7 +453,7 @@ Aig::Aig(Cell *cell) int B = mk.inport("\\B"); int C = mk.inport("\\C"); int D = mk.inport("\\D"); - int Y = mk.nand_gate(mk.nor_gate(A, B), mk.nor_gate(C, D)); + int Y = mk.nand_gate(mk.or_gate(A, B), mk.or_gate(C, D)); mk.outport(Y, "\\Y"); goto optimize; } diff --git a/kernel/celltypes.h b/kernel/celltypes.h index ae88f4aaf..4e91eddda 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -85,6 +85,8 @@ struct CellTypes setup_internals_eval(); IdString A = "\\A", B = "\\B", EN = "\\EN", Y = "\\Y"; + IdString SRC = "\\SRC", DST = "\\DST", DAT = "\\DAT"; + IdString EN_SRC = "\\EN_SRC", EN_DST = "\\EN_DST"; setup_type("$tribuf", {A, EN}, {Y}, true); @@ -99,6 +101,9 @@ struct CellTypes setup_type("$allconst", pool<RTLIL::IdString>(), {Y}, true); setup_type("$allseq", pool<RTLIL::IdString>(), {Y}, true); setup_type("$equiv", {A, B}, {Y}, true); + setup_type("$specify2", {EN, SRC, DST}, pool<RTLIL::IdString>(), true); + setup_type("$specify3", {EN, SRC, DST, DAT}, pool<RTLIL::IdString>(), true); + setup_type("$specrule", {EN_SRC, EN_DST, SRC, DST}, pool<RTLIL::IdString>(), true); } void setup_internals_eval() @@ -464,7 +469,7 @@ struct CellTypes if (cell->type == "$_AOI4_") return eval_not(const_or(const_and(arg1, arg2, false, false, 1), const_and(arg3, arg4, false, false, 1), false, false, 1)); if (cell->type == "$_OAI4_") - return eval_not(const_and(const_or(arg1, arg2, false, false, 1), const_and(arg3, arg4, false, false, 1), false, false, 1)); + return eval_not(const_and(const_or(arg1, arg2, false, false, 1), const_or(arg3, arg4, false, false, 1), false, false, 1)); log_assert(arg4.bits.size() == 0); return eval(cell, arg1, arg2, arg3, errp); diff --git a/kernel/driver.cc b/kernel/driver.cc index 1bc7a5935..f273057dd 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -529,13 +529,13 @@ int main(int argc, char **argv) log_error("Can't open dependencies file for writing: %s\n", strerror(errno)); bool first = true; for (auto fn : yosys_output_files) { - fprintf(f, "%s%s", first ? "" : " ", fn.c_str()); + fprintf(f, "%s%s", first ? "" : " ", escape_filename_spaces(fn).c_str()); first = false; } fprintf(f, ":"); for (auto fn : yosys_input_files) { if (yosys_output_files.count(fn) == 0) - fprintf(f, " %s", fn.c_str()); + fprintf(f, " %s", escape_filename_spaces(fn).c_str()); } fprintf(f, "\n"); } diff --git a/kernel/log.cc b/kernel/log.cc index 9a9104e26..a7820950c 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -230,6 +230,9 @@ static void logv_warning_with_prefix(const char *prefix, } else { + int bak_log_make_debug = log_make_debug; + log_make_debug = 0; + for (auto &re : log_werror_regexes) if (std::regex_search(message, re)) log_error("%s", message.c_str()); @@ -254,6 +257,7 @@ static void logv_warning_with_prefix(const char *prefix, } log_warnings_count++; + log_make_debug = bak_log_make_debug; } } @@ -273,11 +277,22 @@ void log_file_warning(const std::string &filename, int lineno, va_list ap; va_start(ap, format); std::string prefix = stringf("%s:%d: Warning: ", - filename.c_str(), lineno); + filename.c_str(), lineno); logv_warning_with_prefix(prefix.c_str(), format, ap); va_end(ap); } +void log_file_info(const std::string &filename, int lineno, + const char *format, ...) +{ + va_list ap; + va_start(ap, format); + std::string fmt = stringf("%s:%d: Info: %s", + filename.c_str(), lineno, format); + logv(fmt.c_str(), ap); + va_end(ap); +} + YS_ATTRIBUTE(noreturn) static void logv_error_with_prefix(const char *prefix, const char *format, va_list ap) @@ -285,6 +300,9 @@ static void logv_error_with_prefix(const char *prefix, #ifdef EMSCRIPTEN auto backup_log_files = log_files; #endif + int bak_log_make_debug = log_make_debug; + log_make_debug = 0; + log_suppressed(); if (log_errfile != NULL) log_files.push_back(log_errfile); @@ -298,6 +316,8 @@ static void logv_error_with_prefix(const char *prefix, log("%s%s", prefix, log_last_error.c_str()); log_flush(); + log_make_debug = bak_log_make_debug; + if (log_error_atexit) log_error_atexit(); diff --git a/kernel/log.h b/kernel/log.h index e6afae716..3e1facae8 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -80,6 +80,7 @@ void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); // Log with filename to report a problem in a source file. void log_file_warning(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4)); +void log_file_info(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4)); void log_warning_noprefix(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); YS_NORETURN void log_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2), noreturn); diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 7e1159cac..790ba52a3 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -218,15 +218,19 @@ void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id, bool value) { if (value) attributes[id] = RTLIL::Const(1); - else if (attributes.count(id)) - attributes.erase(id); + else { + const auto it = attributes.find(id); + if (it != attributes.end()) + attributes.erase(it); + } } bool RTLIL::AttrObject::get_bool_attribute(RTLIL::IdString id) const { - if (attributes.count(id) == 0) + const auto it = attributes.find(id); + if (it == attributes.end()) return false; - return attributes.at(id).as_bool(); + return it->second.as_bool(); } void RTLIL::AttrObject::set_strpool_attribute(RTLIL::IdString id, const pool<string> &data) @@ -1194,6 +1198,46 @@ namespace { return; } + if (cell->type.in("$specify2", "$specify3")) { + param_bool("\\FULL"); + param_bool("\\SRC_DST_PEN"); + param_bool("\\SRC_DST_POL"); + param("\\T_RISE_MIN"); + param("\\T_RISE_TYP"); + param("\\T_RISE_MAX"); + param("\\T_FALL_MIN"); + param("\\T_FALL_TYP"); + param("\\T_FALL_MAX"); + port("\\EN", 1); + port("\\SRC", param("\\SRC_WIDTH")); + port("\\DST", param("\\DST_WIDTH")); + if (cell->type == "$specify3") { + param_bool("\\EDGE_EN"); + param_bool("\\EDGE_POL"); + param_bool("\\DAT_DST_PEN"); + param_bool("\\DAT_DST_POL"); + port("\\DAT", param("\\DST_WIDTH")); + } + check_expected(); + return; + } + + if (cell->type == "$specrule") { + param("\\TYPE"); + param_bool("\\SRC_PEN"); + param_bool("\\SRC_POL"); + param_bool("\\DST_PEN"); + param_bool("\\DST_POL"); + param("\\T_LIMIT"); + param("\\T_LIMIT2"); + port("\\SRC_EN", 1); + port("\\DST_EN", 1); + port("\\SRC", param("\\SRC_WIDTH")); + port("\\DST", param("\\DST_WIDTH")); + check_expected(); + return; + } + if (cell->type == "$_BUF_") { check_gate("AY"); return; } if (cell->type == "$_NOT_") { check_gate("AY"); return; } if (cell->type == "$_AND_") { check_gate("ABY"); return; } @@ -1470,7 +1514,10 @@ void RTLIL::Module::add(RTLIL::Cell *cell) cell->module = this; } -namespace { +void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires) +{ + log_assert(refcount_wires_ == 0); + struct DeleteWireWorker { RTLIL::Module *module; @@ -1485,17 +1532,29 @@ namespace { } sig = chunks; } - }; -} -void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires) -{ - log_assert(refcount_wires_ == 0); + void operator()(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) { + log_assert(GetSize(lhs) == GetSize(rhs)); + RTLIL::SigSpec new_lhs, new_rhs; + for (int i = 0; i < GetSize(lhs); i++) { + RTLIL::SigBit lhs_bit = lhs[i]; + if (lhs_bit.wire != nullptr && wires_p->count(lhs_bit.wire)) + continue; + RTLIL::SigBit rhs_bit = rhs[i]; + if (rhs_bit.wire != nullptr && wires_p->count(rhs_bit.wire)) + continue; + new_lhs.append(lhs_bit); + new_rhs.append(rhs_bit); + } + lhs = new_lhs; + rhs = new_rhs; + } + }; DeleteWireWorker delete_wire_worker; delete_wire_worker.module = this; delete_wire_worker.wires_p = &wires; - rewrite_sigspecs(delete_wire_worker); + rewrite_sigspecs2(delete_wire_worker); for (auto &it : wires) { log_assert(wires_.count(it->name) != 0); @@ -3456,7 +3515,7 @@ bool RTLIL::SigSpec::operator ==(const RTLIL::SigSpec &other) const pack(); other.pack(); - if (chunks_.size() != chunks_.size()) + if (chunks_.size() != other.chunks_.size()) return false; updhash(); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 757e0dfa4..d3ad57d72 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -50,7 +50,7 @@ namespace RTLIL CONST_FLAG_NONE = 0, CONST_FLAG_STRING = 1, CONST_FLAG_SIGNED = 2, // only used for parameters - CONST_FLAG_REAL = 4 // unused -- to be used for parameters + CONST_FLAG_REAL = 4 // only used for parameters }; struct Const; @@ -524,6 +524,7 @@ struct RTLIL::Const Const(const std::vector<RTLIL::State> &bits) : bits(bits) { flags = CONST_FLAG_NONE; } Const(const std::vector<bool> &bits); Const(const RTLIL::Const &c); + RTLIL::Const &operator =(const RTLIL::Const &other) = default; bool operator <(const RTLIL::Const &other) const; bool operator ==(const RTLIL::Const &other) const; @@ -603,8 +604,10 @@ struct RTLIL::SigChunk SigChunk(RTLIL::State bit, int width = 1); SigChunk(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; } bool operator <(const RTLIL::SigChunk &other) const; bool operator ==(const RTLIL::SigChunk &other) const; @@ -628,6 +631,7 @@ struct RTLIL::SigBit SigBit(const RTLIL::SigChunk &chunk, int index); SigBit(const RTLIL::SigSpec &sig); SigBit(const RTLIL::SigBit &sigbit); + RTLIL::SigBit &operator =(const RTLIL::SigBit &other) = default; bool operator <(const RTLIL::SigBit &other) const; bool operator ==(const RTLIL::SigBit &other) const; @@ -1004,6 +1008,7 @@ public: void fixup_ports(); template<typename T> void rewrite_sigspecs(T &functor); + template<typename T> void rewrite_sigspecs2(T &functor); void cloneInto(RTLIL::Module *new_mod) const; virtual RTLIL::Module *clone() const; @@ -1309,6 +1314,7 @@ public: } template<typename T> void rewrite_sigspecs(T &functor); + template<typename T> void rewrite_sigspecs2(T &functor); #ifdef WITH_PYTHON static std::map<unsigned int, RTLIL::Cell*> *get_all_cells(void); @@ -1327,6 +1333,7 @@ struct RTLIL::CaseRule bool empty() const; template<typename T> void rewrite_sigspecs(T &functor); + template<typename T> void rewrite_sigspecs2(T &functor); RTLIL::CaseRule *clone() const; }; @@ -1340,6 +1347,7 @@ struct RTLIL::SwitchRule : public RTLIL::AttrObject bool empty() const; template<typename T> void rewrite_sigspecs(T &functor); + template<typename T> void rewrite_sigspecs2(T &functor); RTLIL::SwitchRule *clone() const; }; @@ -1350,6 +1358,7 @@ struct RTLIL::SyncRule std::vector<RTLIL::SigSig> actions; template<typename T> void rewrite_sigspecs(T &functor); + template<typename T> void rewrite_sigspecs2(T &functor); RTLIL::SyncRule *clone() const; }; @@ -1362,6 +1371,7 @@ struct RTLIL::Process : public RTLIL::AttrObject ~Process(); template<typename T> void rewrite_sigspecs(T &functor); + template<typename T> void rewrite_sigspecs2(T &functor); RTLIL::Process *clone() const; }; @@ -1424,12 +1434,30 @@ void RTLIL::Module::rewrite_sigspecs(T &functor) } template<typename T> +void RTLIL::Module::rewrite_sigspecs2(T &functor) +{ + for (auto &it : cells_) + it.second->rewrite_sigspecs2(functor); + for (auto &it : processes) + it.second->rewrite_sigspecs2(functor); + for (auto &it : connections_) { + functor(it.first, it.second); + } +} + +template<typename T> void RTLIL::Cell::rewrite_sigspecs(T &functor) { for (auto &it : connections_) functor(it.second); } template<typename T> +void RTLIL::Cell::rewrite_sigspecs2(T &functor) { + for (auto &it : connections_) + functor(it.second); +} + +template<typename T> void RTLIL::CaseRule::rewrite_sigspecs(T &functor) { for (auto &it : compare) functor(it); @@ -1442,6 +1470,17 @@ void RTLIL::CaseRule::rewrite_sigspecs(T &functor) { } template<typename T> +void RTLIL::CaseRule::rewrite_sigspecs2(T &functor) { + for (auto &it : compare) + functor(it); + for (auto &it : actions) { + functor(it.first, it.second); + } + for (auto it : switches) + it->rewrite_sigspecs2(functor); +} + +template<typename T> void RTLIL::SwitchRule::rewrite_sigspecs(T &functor) { functor(signal); @@ -1450,6 +1489,14 @@ void RTLIL::SwitchRule::rewrite_sigspecs(T &functor) } template<typename T> +void RTLIL::SwitchRule::rewrite_sigspecs2(T &functor) +{ + functor(signal); + for (auto it : cases) + it->rewrite_sigspecs2(functor); +} + +template<typename T> void RTLIL::SyncRule::rewrite_sigspecs(T &functor) { functor(signal); @@ -1460,6 +1507,15 @@ void RTLIL::SyncRule::rewrite_sigspecs(T &functor) } template<typename T> +void RTLIL::SyncRule::rewrite_sigspecs2(T &functor) +{ + functor(signal); + for (auto &it : actions) { + functor(it.first, it.second); + } +} + +template<typename T> void RTLIL::Process::rewrite_sigspecs(T &functor) { root_case.rewrite_sigspecs(functor); @@ -1467,6 +1523,14 @@ void RTLIL::Process::rewrite_sigspecs(T &functor) it->rewrite_sigspecs(functor); } +template<typename T> +void RTLIL::Process::rewrite_sigspecs2(T &functor) +{ + root_case.rewrite_sigspecs2(functor); + for (auto it : syncs) + it->rewrite_sigspecs2(functor); +} + YOSYS_NAMESPACE_END #endif diff --git a/kernel/yosys.cc b/kernel/yosys.cc index a12355f1d..377572fc2 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -151,14 +151,16 @@ void yosys_banner() int ceil_log2(int x) { +#if defined(__GNUC__) + return x > 1 ? (8*sizeof(int)) - __builtin_clz(x-1) : 0; +#else if (x <= 0) return 0; - for (int i = 0; i < 32; i++) if (((x-1) >> i) == 0) return i; - log_abort(); +#endif } std::string stringf(const char *fmt, ...) @@ -482,6 +484,20 @@ void remove_directory(std::string dirname) #endif } +std::string escape_filename_spaces(const std::string& filename) +{ + std::string out; + out.reserve(filename.size()); + for (auto c : filename) + { + if (c == ' ') + out += "\\ "; + else + out.push_back(c); + } + return out; +} + int GetSize(RTLIL::Wire *wire) { return wire->width; diff --git a/kernel/yosys.h b/kernel/yosys.h index 2cf6188b4..c7b671724 100644 --- a/kernel/yosys.h +++ b/kernel/yosys.h @@ -244,7 +244,7 @@ extern bool memhasher_active; inline void memhasher() { if (memhasher_active) memhasher_do(); } void yosys_banner(); -int ceil_log2(int x); +int ceil_log2(int x) YS_ATTRIBUTE(const); std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2)); std::string vstringf(const char *fmt, va_list ap); int readsome(std::istream &f, char *s, int n); @@ -257,6 +257,7 @@ std::string make_temp_dir(std::string template_str = "/tmp/yosys_XXXXXX"); bool check_file_exists(std::string filename, bool is_exec = false); bool is_absolute_path(std::string filename); 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); diff --git a/libs/subcircuit/subcircuit.cc b/libs/subcircuit/subcircuit.cc index 7c7236833..e8361a67e 100644 --- a/libs/subcircuit/subcircuit.cc +++ b/libs/subcircuit/subcircuit.cc @@ -320,12 +320,10 @@ class SubCircuit::SolverWorker static int numberOfPermutations(const std::vector<std::string> &list) { - int numPermutations = 1; - for (int i = 0; i < int(list.size()); i++) { - assert(numPermutations < maxPermutationsLimit); - numPermutations *= i+1; - } - return numPermutations; + constexpr size_t mappedPermutationsSize = 10; + constexpr int mappedPermutations[mappedPermutationsSize] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880}; + assert(list.size() < mappedPermutationsSize); + return mappedPermutations[list.size()]; } static void permutateVectorToMap(std::map<std::string, std::string> &map, const std::vector<std::string> &list, int idx) diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex index e64919182..cb1bcf1be 100644 --- a/manual/CHAPTER_CellLib.tex +++ b/manual/CHAPTER_CellLib.tex @@ -466,6 +466,10 @@ Add information about {\tt \$assert}, {\tt \$assume}, {\tt \$live}, {\tt \$fair} \end{fixme} \begin{fixme} +Add information about {\tt \$specify2}, {\tt \$specify3}, and {\tt \$specrule} cells. +\end{fixme} + +\begin{fixme} Add information about {\tt \$slice} and {\tt \$concat} cells. \end{fixme} diff --git a/__init__.py b/misc/__init__.py index 330fd6d86..330fd6d86 100644 --- a/__init__.py +++ b/misc/__init__.py diff --git a/py_wrap_generator.py b/misc/py_wrap_generator.py index 09f934040..9e5727499 100644 --- a/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -2026,7 +2026,6 @@ def gen_wrappers(filename, debug_level_ = 0): #include <boost/python/wrapper.hpp> #include <boost/python/call.hpp> #include <boost/python.hpp> -#include <boost/log/exceptions.hpp> USING_YOSYS_NAMESPACE @@ -2060,7 +2059,6 @@ namespace YOSYS_PYTHON { Yosys::log_streams.push_back(&std::cout); Yosys::log_error_stderr = true; Yosys::yosys_setup(); - Yosys::yosys_banner(); } } diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index 4b22f6d2d..038ab7c7c 100644 --- a/passes/cmds/bugpoint.cc +++ b/passes/cmds/bugpoint.cc @@ -281,6 +281,9 @@ struct BugpointPass : public Pass { } extra_args(args, argidx, design); + if (script.empty()) + log_cmd_error("Missing -script option.\n"); + if (!has_part) { modules = true; @@ -298,7 +301,7 @@ struct BugpointPass : public Pass { if (!check_logfile(grep)) log_cmd_error("The provided grep string is not found in the log file!\n"); - int seed = 0, crashing_seed = seed; + int seed = 0; bool found_something = false, stage2 = false; while (true) { @@ -324,7 +327,6 @@ struct BugpointPass : public Pass { if (crashing_design != design) delete crashing_design; crashing_design = simplified; - crashing_seed = seed; found_something = true; } else diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc index 0ec747671..1128116b4 100644 --- a/passes/cmds/cover.cc +++ b/passes/cmds/cover.cc @@ -98,21 +98,23 @@ struct CoverPass : public Pass { } if ((args[argidx] == "-o" || args[argidx] == "-a" || args[argidx] == "-d") && argidx+1 < args.size()) { const char *open_mode = args[argidx] == "-a" ? "a+" : "w"; - std::string filename = args[++argidx]; + const std::string &filename = args[++argidx]; + FILE *f = nullptr; if (args[argidx-1] == "-d") { #ifdef _WIN32 log_cmd_error("The 'cover -d' option is not supported on win32.\n"); #else char filename_buffer[4096]; snprintf(filename_buffer, 4096, "%s/yosys_cover_%d_XXXXXX.txt", filename.c_str(), getpid()); - filename = mkstemps(filename_buffer, 4); + f = fdopen(mkstemps(filename_buffer, 4), "w"); #endif + } else { + f = fopen(filename.c_str(), open_mode); } - FILE *f = fopen(filename.c_str(), open_mode); if (f == NULL) { for (auto f : out_files) fclose(f); - log_cmd_error("Can't create file %s.\n", args[argidx].c_str()); + log_cmd_error("Can't create file %s%s.\n", args[argidx-1] == "-d" ? "in directory " : "", args[argidx].c_str()); } out_files.push_back(f); continue; diff --git a/passes/cmds/qwp.cc b/passes/cmds/qwp.cc index 1c64a7b77..adbe89e31 100644 --- a/passes/cmds/qwp.cc +++ b/passes/cmds/qwp.cc @@ -291,7 +291,7 @@ struct QwpWorker // gaussian elimination for (int i = 0; i < N; i++) { - if (config.verbose && ((i+1) % (N/15)) == 0) + if (config.verbose && N > 15 && ((i+1) % (N/15)) == 0) log("> Solved %d%%: %d/%d\n", (100*(i+1))/N, i+1, N); // find best row diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc index f6949c820..3eedc86b8 100644 --- a/passes/cmds/setundef.cc +++ b/passes/cmds/setundef.cc @@ -393,44 +393,112 @@ struct SetundefPass : public Pass { ffbits.insert(bit); } - for (auto wire : module->wires()) + auto process_initwires = [&]() { - if (!wire->attributes.count("\\init")) - continue; + dict<Wire*, int> wire_weights; - for (auto bit : sigmap(wire)) - ffbits.erase(bit); + for (auto wire : initwires) + { + int weight = 0; - initwires.insert(wire); - } + for (auto bit : sigmap(wire)) + weight += ffbits.count(bit) ? +1 : -1; + + wire_weights[wire] = weight; + } + + initwires.sort([&](Wire *a, Wire *b) { return wire_weights.at(a) > wire_weights.at(b); }); + + for (auto wire : initwires) + { + Const &initval = wire->attributes["\\init"]; + initval.bits.resize(GetSize(wire), State::Sx); + + for (int i = 0; i < GetSize(wire); i++) { + SigBit bit = sigmap(SigBit(wire, i)); + if (initval[i] == State::Sx && ffbits.count(bit)) { + initval[i] = worker.next_bit(); + ffbits.erase(bit); + } + } + + if (initval.is_fully_undef()) + wire->attributes.erase("\\init"); + } + + initwires.clear(); + }; for (int wire_types = 0; wire_types < 2; wire_types++) - for (auto wire : module->wires()) + { + // prioritize wires that already have an init attribute + if (!ffbits.empty()) { - if (wire->name[0] == (wire_types ? '\\' : '$')) - next_wire: - continue; + for (auto wire : module->wires()) + { + if (wire->name[0] == (wire_types ? '\\' : '$')) + continue; - for (auto bit : sigmap(wire)) - if (!ffbits.count(bit)) - goto next_wire; + if (!wire->attributes.count("\\init")) + continue; - for (auto bit : sigmap(wire)) - ffbits.erase(bit); + Const &initval = wire->attributes["\\init"]; + initval.bits.resize(GetSize(wire), State::Sx); + + if (initval.is_fully_undef()) { + wire->attributes.erase("\\init"); + continue; + } - initwires.insert(wire); + for (int i = 0; i < GetSize(wire); i++) + if (initval[i] != State::Sx) + ffbits.erase(sigmap(SigBit(wire, i))); + + initwires.insert(wire); + } + + process_initwires(); } - for (auto wire : initwires) - { - Const &initval = wire->attributes["\\init"]; + // next consider wires that completely contain bits to be initialized + if (!ffbits.empty()) + { + for (auto wire : module->wires()) + { + if (wire->name[0] == (wire_types ? '\\' : '$')) + continue; - for (int i = 0; i < GetSize(wire); i++) - if (GetSize(initval) <= i) - initval.bits.push_back(worker.next_bit()); - else if (initval.bits[i] == State::Sx) - initval.bits[i] = worker.next_bit(); + for (auto bit : sigmap(wire)) + if (!ffbits.count(bit)) + goto next_wire; + + initwires.insert(wire); + + next_wire: + continue; + } + + process_initwires(); + } + + // finally use whatever wire we can find. + if (!ffbits.empty()) + { + for (auto wire : module->wires()) + { + if (wire->name[0] == (wire_types ? '\\' : '$')) + continue; + + for (auto bit : sigmap(wire)) + if (ffbits.count(bit)) + initwires.insert(wire); + } + + process_initwires(); + } } + + log_assert(ffbits.empty()); } module->rewrite_sigspecs(worker); diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 54f4ea817..d22685b62 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -37,7 +37,9 @@ struct statdata_t 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::set<RTLIL::IdString> unknown_cell_area; @@ -70,8 +72,10 @@ struct statdata_t #undef X } - statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area) + statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area, string techname) { + tech = techname; + #define X(_name) _name = 0; STAT_NUMERIC_MEMBERS #undef X @@ -153,7 +157,8 @@ struct statdata_t log(" Number of processes: %6d\n", num_processes); log(" Number of cells: %6d\n", num_cells); for (auto &it : num_cells_by_type) - log(" %-26s %6d\n", RTLIL::id2cstr(it.first), it.second); + if (it.second) + log(" %-26s %6d\n", RTLIL::id2cstr(it.first), it.second); if (!unknown_cell_area.empty()) { log("\n"); @@ -165,6 +170,59 @@ struct statdata_t log("\n"); log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), area); } + + if (tech == "xilinx") + { + int lut6_cnt = num_cells_by_type["\\LUT6"]; + int lut5_cnt = num_cells_by_type["\\LUT5"]; + int lut4_cnt = num_cells_by_type["\\LUT4"]; + int lut3_cnt = num_cells_by_type["\\LUT3"]; + int lut2_cnt = num_cells_by_type["\\LUT2"]; + int lut1_cnt = num_cells_by_type["\\LUT1"]; + int lc_cnt = 0; + + lc_cnt += lut6_cnt; + + lc_cnt += lut5_cnt; + if (lut1_cnt) { + int cnt = std::min(lut5_cnt, lut1_cnt); + lut5_cnt -= cnt; + lut1_cnt -= cnt; + } + + lc_cnt += lut4_cnt; + if (lut1_cnt) { + int cnt = std::min(lut4_cnt, lut1_cnt); + lut4_cnt -= cnt; + lut1_cnt -= cnt; + } + if (lut2_cnt) { + int cnt = std::min(lut4_cnt, lut2_cnt); + lut4_cnt -= cnt; + lut2_cnt -= cnt; + } + + lc_cnt += lut3_cnt; + if (lut1_cnt) { + int cnt = std::min(lut3_cnt, lut1_cnt); + lut3_cnt -= cnt; + lut1_cnt -= cnt; + } + if (lut2_cnt) { + int cnt = std::min(lut3_cnt, lut2_cnt); + lut3_cnt -= cnt; + lut2_cnt -= cnt; + } + if (lut3_cnt) { + int cnt = (lut3_cnt + 1) / 2; + lut3_cnt -= cnt; + } + + lc_cnt += (lut2_cnt + lut1_cnt + 1) / 2; + + log("\n"); + log(" Estimated number of LCs: %10d\n", lc_cnt); + } } }; @@ -226,6 +284,10 @@ struct StatPass : public Pass { log(" -liberty <liberty_file>\n"); log(" use cell area information from the provided liberty file\n"); log("\n"); + log(" -tech <technology>\n"); + log(" print area estemate for the specified technology. Corrently supported\n"); + log(" calues for <technology>: xilinx\n"); + log("\n"); log(" -width\n"); log(" annotate internal cell types with their word width.\n"); log(" e.g. $add_8 for an 8 bit wide $add cell.\n"); @@ -239,6 +301,7 @@ struct StatPass : public Pass { RTLIL::Module *top_mod = NULL; std::map<RTLIL::IdString, statdata_t> mod_stat; dict<IdString, double> cell_area; + string techname; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -253,6 +316,10 @@ struct StatPass : public Pass { read_liberty_cellarea(cell_area, liberty_file); continue; } + if (args[argidx] == "-tech" && argidx+1 < args.size()) { + techname = args[++argidx]; + continue; + } if (args[argidx] == "-top" && argidx+1 < args.size()) { if (design->modules_.count(RTLIL::escape_id(args[argidx+1])) == 0) log_cmd_error("Can't find module %s.\n", args[argidx+1].c_str()); @@ -263,13 +330,16 @@ struct StatPass : public Pass { } extra_args(args, argidx, design); + if (techname != "" && techname != "xilinx") + log_cmd_error("Unsupported technology: '%s'\n", techname.c_str()); + for (auto mod : design->selected_modules()) { if (!top_mod && design->full_selection()) if (mod->get_bool_attribute("\\top")) top_mod = mod; - statdata_t data(design, mod, width_mode, cell_area); + statdata_t data(design, mod, width_mode, cell_area, techname); mod_stat[mod->name] = data; log("\n"); diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc index ee96ace86..1a44bdaec 100644 --- a/passes/cmds/tee.cc +++ b/passes/cmds/tee.cc @@ -52,7 +52,9 @@ struct TeePass : public Pass { void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::vector<FILE*> backup_log_files, files_to_close; + std::vector<std::ostream*> backup_log_streams; int backup_log_verbose_level = log_verbose_level; + backup_log_streams = log_streams; backup_log_files = log_files; size_t argidx; @@ -60,6 +62,7 @@ struct TeePass : public Pass { { if (args[argidx] == "-q" && files_to_close.empty()) { log_files.clear(); + log_streams.clear(); continue; } if ((args[argidx] == "-o" || args[argidx] == "-a") && argidx+1 < args.size()) { @@ -89,6 +92,7 @@ struct TeePass : public Pass { for (auto cf : files_to_close) fclose(cf); log_files = backup_log_files; + log_streams = backup_log_streams; throw; } @@ -97,6 +101,7 @@ struct TeePass : public Pass { log_verbose_level = backup_log_verbose_level; log_files = backup_log_files; + log_streams = backup_log_streams; } } TeePass; diff --git a/passes/equiv/equiv_opt.cc b/passes/equiv/equiv_opt.cc index e5dda9c24..3596dfd7b 100644 --- a/passes/equiv/equiv_opt.cc +++ b/passes/equiv/equiv_opt.cc @@ -44,7 +44,10 @@ struct EquivOptPass:public ScriptPass log(" useful for handling architecture-specific primitives.\n"); log("\n"); log(" -assert\n"); - log(" produce an error if the circuits are not equivalent\n"); + log(" produce an error if the circuits are not equivalent.\n"); + log("\n"); + log(" -undef\n"); + log(" enable modelling of undef states during equiv_induct.\n"); log("\n"); log("The following commands are executed by this verification command:\n"); help_script(); @@ -52,13 +55,14 @@ struct EquivOptPass:public ScriptPass } std::string command, techmap_opts; - bool assert; + bool assert, undef; void clear_flags() YS_OVERRIDE { command = ""; techmap_opts = ""; assert = false; + undef = false; } void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE @@ -84,6 +88,10 @@ struct EquivOptPass:public ScriptPass assert = true; continue; } + if (args[argidx] == "-undef") { + undef = true; + continue; + } break; } @@ -139,7 +147,12 @@ struct EquivOptPass:public ScriptPass if (check_label("prove")) { run("equiv_make gold gate equiv"); - run("equiv_induct equiv"); + if (help_mode) + run("equiv_induct [-undef] equiv"); + else if (undef) + run("equiv_induct -undef equiv"); + else + run("equiv_induct equiv"); if (help_mode) run("equiv_status [-assert] equiv"); else if (assert) diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index b8ff99884..24e64a9b2 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -562,7 +562,8 @@ struct HierarchyPass : public Pass { log("In parametric designs, a module might exists in several variations with\n"); log("different parameter values. This pass looks at all modules in the current\n"); log("design an re-runs the language frontends for the parametric modules as\n"); - log("needed.\n"); + log("needed. It also resolves assignments to wired logic data types (wand/wor),\n"); + log("resolves positional module parameters, unroll array instances, and more.\n"); log("\n"); log(" -check\n"); log(" also check the design hierarchy. this generates an error when\n"); @@ -570,7 +571,7 @@ struct HierarchyPass : public Pass { log("\n"); log(" -simcheck\n"); 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(" instantiated, and throw an error if the design has no top module.\n"); log("\n"); log(" -purge_lib\n"); log(" by default the hierarchy command will not remove library (blackbox)\n"); @@ -583,20 +584,20 @@ struct HierarchyPass : public Pass { log("\n"); log(" -keep_positionals\n"); log(" per default this pass also converts positional arguments in cells\n"); - log(" to arguments using port names. this option disables this behavior.\n"); + log(" to arguments using port names. This option disables this behavior.\n"); log("\n"); log(" -keep_portwidths\n"); log(" per default this pass adjusts the port width on cells that are\n"); - log(" module instances when the width does not match the module port. this\n"); + log(" module instances when the width does not match the module port. This\n"); log(" option disables this behavior.\n"); log("\n"); log(" -nokeep_asserts\n"); log(" per default this pass sets the \"keep\" attribute on all modules\n"); - log(" that directly or indirectly contain one or more $assert cells. this\n"); - log(" option disables this behavior.\n"); + log(" that directly or indirectly contain one or more formal properties.\n"); + log(" This option disables this behavior.\n"); log("\n"); log(" -top <module>\n"); - log(" use the specified top module to built a design hierarchy. modules\n"); + log(" use the specified top module to build the design hierarchy. Modules\n"); log(" outside this tree (unused modules) are removed.\n"); log("\n"); log(" when the -top option is used, the 'top' attribute will be set on the\n"); @@ -606,6 +607,12 @@ struct HierarchyPass : public Pass { log(" -auto-top\n"); log(" automatically determine the top of the design hierarchy and mark it.\n"); log("\n"); + log(" -chparam name value \n"); + log(" elaborate the top module using this parameter value. Modules on which\n"); + log(" this parameter does not exist may cause a warning message to be output.\n"); + log(" This option can be specified multiple times to override multiple\n"); + log(" parameters. String values must be passed in double quotes (\").\n"); + log("\n"); log("In -generate mode this pass generates blackbox modules for the given cell\n"); log("types (wildcards supported). For this the design is searched for cells that\n"); log("match the given types and then the given port declarations are used to\n"); @@ -641,6 +648,7 @@ struct HierarchyPass : public Pass { bool nokeep_asserts = false; std::vector<std::string> generate_cells; std::vector<generate_port_decl_t> generate_ports; + std::map<std::string, std::string> parameters; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -715,28 +723,61 @@ struct HierarchyPass : public Pass { if (args[argidx] == "-top") { if (++argidx >= args.size()) log_cmd_error("Option -top requires an additional argument!\n"); - top_mod = design->modules_.count(RTLIL::escape_id(args[argidx])) ? design->modules_.at(RTLIL::escape_id(args[argidx])) : NULL; - if (top_mod == NULL && design->modules_.count("$abstract" + RTLIL::escape_id(args[argidx]))) { - dict<RTLIL::IdString, RTLIL::Const> empty_parameters; - design->modules_.at("$abstract" + RTLIL::escape_id(args[argidx]))->derive(design, empty_parameters); - top_mod = design->modules_.count(RTLIL::escape_id(args[argidx])) ? design->modules_.at(RTLIL::escape_id(args[argidx])) : NULL; - } - if (top_mod == NULL) - load_top_mod = args[argidx]; + load_top_mod = args[argidx]; continue; } if (args[argidx] == "-auto-top") { auto_top_mode = true; continue; } + if (args[argidx] == "-chparam" && argidx+2 < args.size()) { + const std::string &key = args[++argidx]; + const std::string &value = args[++argidx]; + auto r = parameters.emplace(key, value); + if (!r.second) { + log_warning("-chparam %s already specified: overwriting.\n", key.c_str()); + r.first->second = value; + } + continue; + } break; } extra_args(args, argidx, design, false); - if (!load_top_mod.empty()) { + if (!load_top_mod.empty()) + { + IdString top_name = RTLIL::escape_id(load_top_mod); + IdString abstract_id = "$abstract" + RTLIL::escape_id(load_top_mod); + top_mod = design->module(top_name); + + dict<RTLIL::IdString, RTLIL::Const> top_parameters; + for (auto ¶ : parameters) { + SigSpec sig_value; + if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second)) + log_cmd_error("Can't decode value '%s'!\n", para.second.c_str()); + top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const(); + } + + if (top_mod == nullptr && design->module(abstract_id)) + top_mod = design->module(design->module(abstract_id)->derive(design, top_parameters)); + else if (top_mod != nullptr && !top_parameters.empty()) + top_mod = design->module(top_mod->derive(design, top_parameters)); + + if (top_mod != nullptr && top_mod->name != top_name) { + Module *m = top_mod->clone(); + m->name = top_name; + Module *old_mod = design->module(top_name); + if (old_mod) + design->remove(old_mod); + design->add(m); + top_mod = m; + } + } + + if (top_mod == nullptr && !load_top_mod.empty()) { #ifdef YOSYS_ENABLE_VERIFIC if (verific_import_pending) { - verific_import(design, load_top_mod); + verific_import(design, parameters, load_top_mod); top_mod = design->module(RTLIL::escape_id(load_top_mod)); } #endif @@ -745,7 +786,7 @@ struct HierarchyPass : public Pass { } else { #ifdef YOSYS_ENABLE_VERIFIC if (verific_import_pending) - verific_import(design); + verific_import(design, parameters); #endif } @@ -846,7 +887,7 @@ struct HierarchyPass : public Pass { std::map<RTLIL::Module*, bool> cache; for (auto mod : design->modules()) if (set_keep_assert(cache, mod)) { - log("Module %s directly or indirectly contains $assert cells -> setting \"keep\" attribute.\n", log_id(mod)); + log("Module %s directly or indirectly contains formal properties -> setting \"keep\" attribute.\n", log_id(mod)); mod->set_bool_attribute("\\keep"); } } @@ -903,62 +944,178 @@ struct HierarchyPass : public Pass { std::vector<Module*> design_modules = design->modules(); for (auto module : design_modules) - for (auto cell : module->cells()) { - Module *m = design->module(cell->type); + pool<Wire*> wand_wor_index; + dict<Wire*, SigSpec> wand_map, wor_map; + vector<SigSig> new_connections; - if (m == nullptr) - continue; + for (auto wire : module->wires()) + { + if (wire->get_bool_attribute("\\wand")) { + wand_map[wire] = SigSpec(); + wand_wor_index.insert(wire); + } + if (wire->get_bool_attribute("\\wor")) { + wor_map[wire] = SigSpec(); + wand_wor_index.insert(wire); + } + } + + for (auto &conn : module->connections()) + { + SigSig new_conn; + int cursor = 0; - if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) { - IdString new_m_name = m->derive(design, cell->parameters, true); - if (new_m_name.empty()) + for (auto c : conn.first.chunks()) + { + Wire *w = c.wire; + SigSpec rhs = conn.second.extract(cursor, GetSize(c)); + + if (wand_wor_index.count(w) == 0) { + new_conn.first.append(c); + new_conn.second.append(rhs); + } else { + if (wand_map.count(w)) { + SigSpec sig = SigSpec(State::S1, GetSize(w)); + sig.replace(c.offset, rhs); + wand_map.at(w).append(sig); + } else { + SigSpec sig = SigSpec(State::S0, GetSize(w)); + sig.replace(c.offset, rhs); + wor_map.at(w).append(sig); + } + } + cursor += GetSize(c); + } + new_connections.push_back(new_conn); + } + module->new_connections(new_connections); + + for (auto cell : module->cells()) + { + if (!cell->known()) continue; - if (new_m_name != m->name) { - m = design->module(new_m_name); - blackbox_derivatives.insert(m); + + for (auto &conn : cell->connections()) + { + if (!cell->output(conn.first)) + continue; + + SigSpec new_sig; + bool update_port = false; + + for (auto c : conn.second.chunks()) + { + Wire *w = c.wire; + + if (wand_wor_index.count(w) == 0) { + new_sig.append(c); + continue; + } + + Wire *t = module->addWire(NEW_ID, GetSize(c)); + new_sig.append(t); + update_port = true; + + if (wand_map.count(w)) { + SigSpec sig = SigSpec(State::S1, GetSize(w)); + sig.replace(c.offset, t); + wand_map.at(w).append(sig); + } else { + SigSpec sig = SigSpec(State::S0, GetSize(w)); + sig.replace(c.offset, t); + wor_map.at(w).append(sig); + } + } + + if (update_port) + cell->setPort(conn.first, new_sig); } } - for (auto &conn : cell->connections()) + for (auto w : wand_wor_index) { - Wire *w = m->wire(conn.first); + bool wand = wand_map.count(w); + SigSpec sigs = wand ? wand_map.at(w) : wor_map.at(w); - if (w == nullptr || w->port_id == 0) + if (GetSize(sigs) == 0) continue; - if (GetSize(conn.second) == 0) + if (GetSize(w) == 1) { + if (wand) + module->addReduceAnd(NEW_ID, sigs, w); + else + module->addReduceOr(NEW_ID, sigs, w); continue; + } + + SigSpec s = sigs.extract(0, GetSize(w)); + for (int i = GetSize(w); i < GetSize(sigs); i += GetSize(w)) { + if (wand) + s = module->And(NEW_ID, s, sigs.extract(i, GetSize(w))); + else + s = module->Or(NEW_ID, s, sigs.extract(i, GetSize(w))); + } + module->connect(w, s); + } - SigSpec sig = conn.second; + for (auto cell : module->cells()) + { + Module *m = design->module(cell->type); - if (!keep_portwidths && GetSize(w) != GetSize(conn.second)) - { - if (GetSize(w) < GetSize(conn.second)) - { - int n = GetSize(conn.second) - GetSize(w); - if (!w->port_input && w->port_output) - module->connect(sig.extract(GetSize(w), n), Const(0, n)); - sig.remove(GetSize(w), n); + if (m == nullptr) + continue; + + if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) { + IdString new_m_name = m->derive(design, cell->parameters, true); + if (new_m_name.empty()) + continue; + if (new_m_name != m->name) { + m = design->module(new_m_name); + blackbox_derivatives.insert(m); } - else + } + + for (auto &conn : cell->connections()) + { + Wire *w = m->wire(conn.first); + + if (w == nullptr || w->port_id == 0) + continue; + + if (GetSize(conn.second) == 0) + continue; + + SigSpec sig = conn.second; + + if (!keep_portwidths && GetSize(w) != GetSize(conn.second)) { - int n = GetSize(w) - GetSize(conn.second); - if (w->port_input && !w->port_output) - sig.append(Const(0, n)); + if (GetSize(w) < GetSize(conn.second)) + { + int n = GetSize(conn.second) - GetSize(w); + if (!w->port_input && w->port_output) + module->connect(sig.extract(GetSize(w), n), Const(0, n)); + sig.remove(GetSize(w), n); + } else - sig.append(module->addWire(NEW_ID, n)); + { + int n = GetSize(w) - GetSize(conn.second); + if (w->port_input && !w->port_output) + sig.append(Const(0, n)); + else + sig.append(module->addWire(NEW_ID, n)); + } + + if (!conn.second.is_fully_const() || !w->port_input || w->port_output) + log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell), + log_id(conn.first), GetSize(conn.second), GetSize(sig)); + cell->setPort(conn.first, sig); } - if (!conn.second.is_fully_const() || !w->port_input || w->port_output) - log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell), - log_id(conn.first), GetSize(conn.second), GetSize(sig)); - cell->setPort(conn.first, sig); + if (w->port_output && !w->port_input && sig.has_const()) + log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n", + log_id(module), log_id(cell), log_id(conn.first), log_id(cell->type), log_signal(sig)); } - - if (w->port_output && !w->port_input && sig.has_const()) - log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n", - log_id(module), log_id(cell), log_id(conn.first), log_id(cell->type), log_signal(sig)); } } diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index c38e9df5e..cfb0f788a 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -64,7 +64,7 @@ struct keep_cache_t bool query(Cell *cell) { - if (cell->type.in("$memwr", "$meminit", "$assert", "$assume", "$live", "$fair", "$cover")) + if (cell->type.in("$memwr", "$meminit", "$assert", "$assume", "$live", "$fair", "$cover", "$specify2", "$specify3", "$specrule")) return true; if (cell->has_keep_attr()) @@ -85,22 +85,34 @@ void rmunused_module_cells(Module *module, bool verbose) { SigMap sigmap(module); pool<Cell*> queue, unused; + pool<SigBit> used_raw_bits; dict<SigBit, pool<Cell*>> wire2driver; + dict<SigBit, vector<string>> driver_driver_logs; + + SigMap raw_sigmap; + for (auto &it : module->connections_) { + for (int i = 0; i < GetSize(it.second); i++) { + if (it.second[i].wire != nullptr) + raw_sigmap.add(it.first[i], it.second[i]); + } + } for (auto &it : module->cells_) { Cell *cell = it.second; for (auto &it2 : cell->connections()) { - if (!ct_all.cell_known(cell->type) || ct_all.cell_output(cell->type, it2.first)) - for (auto raw_bit : it2.second) { - if (raw_bit.wire == nullptr) - continue; - auto bit = sigmap(raw_bit); - if (bit.wire == nullptr) - log_warning("Driver-driver conflict for %s between cell %s.%s and constant %s in %s: Resolved using constant.\n", - log_signal(raw_bit), log_id(cell), log_id(it2.first), log_signal(bit), log_id(module)); - if (bit.wire != nullptr) - wire2driver[bit].insert(cell); - } + if (ct_all.cell_known(cell->type) && !ct_all.cell_output(cell->type, it2.first)) + continue; + for (auto raw_bit : it2.second) { + if (raw_bit.wire == nullptr) + continue; + auto bit = sigmap(raw_bit); + if (bit.wire == nullptr && ct_all.cell_known(cell->type)) + driver_driver_logs[raw_sigmap(raw_bit)].push_back(stringf("Driver-driver conflict " + "for %s between cell %s.%s and constant %s in %s: Resolved using constant.", + log_signal(raw_bit), log_id(cell), log_id(it2.first), log_signal(bit), log_id(module))); + if (bit.wire != nullptr) + wire2driver[bit].insert(cell); + } } if (keep_cache.query(cell)) queue.insert(cell); @@ -114,6 +126,8 @@ void rmunused_module_cells(Module *module, bool verbose) for (auto bit : sigmap(wire)) for (auto c : wire2driver[bit]) queue.insert(c), unused.erase(c); + for (auto raw_bit : SigSpec(wire)) + used_raw_bits.insert(raw_sigmap(raw_bit)); } } @@ -142,6 +156,22 @@ void rmunused_module_cells(Module *module, bool verbose) module->remove(cell); count_rm_cells++; } + + for (auto &it : module->cells_) { + Cell *cell = it.second; + for (auto &it2 : cell->connections()) { + if (ct_all.cell_known(cell->type) && !ct_all.cell_input(cell->type, it2.first)) + continue; + for (auto raw_bit : raw_sigmap(it2.second)) + used_raw_bits.insert(raw_bit); + } + } + + for (auto it : driver_driver_logs) { + if (used_raw_bits.count(it.first)) + for (auto msg : it.second) + log_warning("%s\n", msg.c_str()); + } } int count_nontrivial_wire_attrs(RTLIL::Wire *w) @@ -202,7 +232,7 @@ bool check_public_name(RTLIL::IdString id) return true; } -void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose) +bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose) { SigPool register_signals; SigPool connected_signals; @@ -245,11 +275,13 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos module->connections_.clear(); SigPool used_signals; + SigPool raw_used_signals; SigPool used_signals_nodrivers; for (auto &it : module->cells_) { RTLIL::Cell *cell = it.second; for (auto &it2 : cell->connections_) { assign_map.apply(it2.second); + raw_used_signals.add(it2.second); used_signals.add(it2.second); if (!ct_all.cell_output(cell->type, it2.first)) used_signals_nodrivers.add(it2.second); @@ -259,6 +291,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos RTLIL::Wire *wire = it.second; if (wire->port_id > 0) { RTLIL::SigSpec sig = RTLIL::SigSpec(wire); + raw_used_signals.add(sig); assign_map.apply(sig); used_signals.add(sig); if (!wire->port_input) @@ -271,72 +304,103 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos } } - std::vector<RTLIL::Wire*> maybe_del_wires; + pool<RTLIL::Wire*> del_wires_queue; for (auto wire : module->wires()) { - if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep") || wire->attributes.count("\\init")) { - RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1; - assign_map.apply(s2); - if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep")) { - maybe_del_wires.push_back(wire); - } else { - log_assert(GetSize(s1) == GetSize(s2)); - RTLIL::SigSig new_conn; - for (int i = 0; i < GetSize(s1); i++) - if (s1[i] != s2[i]) { - new_conn.first.append_bit(s1[i]); - new_conn.second.append_bit(s2[i]); + SigSpec s1 = SigSpec(wire), s2 = assign_map(s1); + log_assert(GetSize(s1) == GetSize(s2)); + + Const initval; + if (wire->attributes.count("\\init")) + initval = wire->attributes.at("\\init"); + if (GetSize(initval) != GetSize(wire)) + initval.bits.resize(GetSize(wire), State::Sx); + if (initval.is_fully_undef()) + wire->attributes.erase("\\init"); + + if (GetSize(wire) == 0) { + // delete zero-width wires, unless they are module ports + if (wire->port_id == 0) + goto delete_this_wire; + } else + if (wire->port_id != 0 || wire->get_bool_attribute("\\keep") || !initval.is_fully_undef()) { + // do not delete anything with "keep" or module ports or initialized wires + } else + if (!purge_mode && check_public_name(wire->name)) { + // do not get rid of public names unless in purge mode + } else + if (!raw_used_signals.check_any(s1)) { + // delete wires that aren't used by anything directly + goto delete_this_wire; + } else + if (!used_signals.check_any(s2)) { + // delete wires that aren't used by anything indirectly, even though other wires may alias it + goto delete_this_wire; + } + + if (0) + { + delete_this_wire: + del_wires_queue.insert(wire); + } + else + { + RTLIL::SigSig new_conn; + for (int i = 0; i < GetSize(s1); i++) + if (s1[i] != s2[i]) { + if (s2[i] == State::Sx && (initval[i] == State::S0 || initval[i] == State::S1)) { + s2[i] = initval[i]; + initval[i] = State::Sx; } - if (new_conn.first.size() > 0) { - used_signals.add(new_conn.first); - used_signals.add(new_conn.second); - module->connect(new_conn); + new_conn.first.append_bit(s1[i]); + new_conn.second.append_bit(s2[i]); } + if (new_conn.first.size() > 0) { + if (initval.is_fully_undef()) + wire->attributes.erase("\\init"); + else + wire->attributes.at("\\init") = initval; + used_signals.add(new_conn.first); + used_signals.add(new_conn.second); + module->connect(new_conn); } - } else { - if (!used_signals.check_any(RTLIL::SigSpec(wire))) - maybe_del_wires.push_back(wire); - } - RTLIL::SigSpec sig = assign_map(RTLIL::SigSpec(wire)); - if (!used_signals_nodrivers.check_any(sig)) { - std::string unused_bits; - for (int i = 0; i < GetSize(sig); i++) { - if (sig[i].wire == NULL) - continue; - if (!used_signals_nodrivers.check(sig[i])) { - if (!unused_bits.empty()) - unused_bits += " "; - unused_bits += stringf("%d", i); + if (!used_signals_nodrivers.check_all(s2)) { + std::string unused_bits; + for (int i = 0; i < GetSize(s2); i++) { + if (s2[i].wire == NULL) + continue; + if (!used_signals_nodrivers.check(s2[i])) { + if (!unused_bits.empty()) + unused_bits += " "; + unused_bits += stringf("%d", i); + } } - } - if (unused_bits.empty() || wire->port_id != 0) + if (unused_bits.empty() || wire->port_id != 0) + wire->attributes.erase("\\unused_bits"); + else + wire->attributes["\\unused_bits"] = RTLIL::Const(unused_bits); + } else { wire->attributes.erase("\\unused_bits"); - else - wire->attributes["\\unused_bits"] = RTLIL::Const(unused_bits); - } else { - wire->attributes.erase("\\unused_bits"); + } } } + int del_temp_wires_count = 0; + for (auto wire : del_wires_queue) { + if (ys_debug() || (check_public_name(wire->name) && verbose)) + log_debug(" removing unused non-port wire %s.\n", wire->name.c_str()); + else + del_temp_wires_count++; + } - pool<RTLIL::Wire*> del_wires; + module->remove(del_wires_queue); + count_rm_wires += GetSize(del_wires_queue); - int del_wires_count = 0; - for (auto wire : maybe_del_wires) - if (!used_signals.check_any(RTLIL::SigSpec(wire))) { - if (check_public_name(wire->name) && verbose) { - log_debug(" removing unused non-port wire %s.\n", wire->name.c_str()); - } - del_wires.insert(wire); - del_wires_count++; - } + if (verbose && del_temp_wires_count) + log_debug(" removed %d unused temporary wires.\n", del_temp_wires_count); - module->remove(del_wires); - count_rm_wires += del_wires.size(); - - if (verbose && del_wires_count > 0) - log_debug(" removed %d unused temporary wires.\n", del_wires_count); + return !del_wires_queue.empty(); } bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose) @@ -434,10 +498,10 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool module->design->scratchpad_set_bool("opt.did_something", true); rmunused_module_cells(module, verbose); - rmunused_module_signals(module, purge_mode, verbose); + while (rmunused_module_signals(module, purge_mode, verbose)) { } if (rminit && rmunused_module_init(module, purge_mode, verbose)) - rmunused_module_signals(module, purge_mode, verbose); + while (rmunused_module_signals(module, purge_mode, verbose)) { } } struct OptCleanPass : public Pass { @@ -483,6 +547,9 @@ struct OptCleanPass : public Pass { ct_all.setup(design); + count_rm_cells = 0; + count_rm_wires = 0; + for (auto module : design->selected_whole_modules_warn()) { if (module->has_processes_warn()) continue; @@ -548,7 +615,7 @@ struct CleanPass : public Pass { for (auto module : design->selected_whole_modules()) { if (module->has_processes()) continue; - rmunused_module(module, purge_mode, false, false); + rmunused_module(module, purge_mode, ys_debug(), false); } log_suppressed(); diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index af6d352af..512ef0cbf 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -39,6 +39,9 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) SigPool used_signals; SigPool all_signals; + dict<SigBit, pair<Wire*, State>> initbits; + pool<Wire*> revisit_initwires; + for (auto cell : module->cells()) for (auto &conn : cell->connections()) { if (!ct.cell_known(cell->type) || ct.cell_output(cell->type, conn.first)) @@ -48,9 +51,17 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) } for (auto wire : module->wires()) { + if (wire->attributes.count("\\init")) { + SigSpec sig = sigmap(wire); + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++) { + if (initval[i] == State::S0 || initval[i] == State::S1) + initbits[sig[i]] = make_pair(wire, initval[i]); + } + } if (wire->port_input) driven_signals.add(sigmap(wire)); - if (wire->port_output) + if (wire->port_output || wire->get_bool_attribute("\\keep")) used_signals.add(sigmap(wire)); all_signals.add(sigmap(wire)); } @@ -67,10 +78,43 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module) if (sig.size() == 0) continue; - log_debug("Setting undriven signal in %s to undef: %s\n", RTLIL::id2cstr(module->name), log_signal(c)); - module->connect(RTLIL::SigSig(c, RTLIL::SigSpec(RTLIL::State::Sx, c.width))); + Const val(RTLIL::State::Sx, GetSize(sig)); + for (int i = 0; i < GetSize(sig); i++) { + SigBit bit = sigmap(sig[i]); + auto cursor = initbits.find(bit); + if (cursor != initbits.end()) { + revisit_initwires.insert(cursor->second.first); + val[i] = cursor->second.second; + } + } + + log_debug("Setting undriven signal in %s to constant: %s = %s\n", log_id(module), log_signal(sig), log_signal(val)); + module->connect(sig, val); did_something = true; } + + if (!revisit_initwires.empty()) + { + SigMap sm2(module); + + for (auto wire : revisit_initwires) { + SigSpec sig = sm2(wire); + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++) { + if (SigBit(initval[i]) == sig[i]) + initval[i] = State::Sx; + } + if (initval.is_fully_undef()) { + log_debug("Removing init attribute from %s/%s.\n", log_id(module), log_id(wire)); + wire->attributes.erase("\\init"); + did_something = true; + } else if (initval != wire->attributes.at("\\init")) { + log_debug("Updating init attribute on %s/%s: %s\n", log_id(module), log_id(wire), log_signal(initval)); + wire->attributes["\\init"] = initval; + did_something = true; + } + } + } } void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val) diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index dbebf21e0..6511e091b 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -184,6 +184,10 @@ struct OptMuxtreeWorker log_debug(" Root of a mux tree: %s%s\n", log_id(mux2info[mux_idx].cell), root_enable_muxes.at(mux_idx) ? " (pure)" : ""); root_mux_rerun.erase(mux_idx); eval_root_mux(mux_idx); + if (glob_abort_cnt == 0) { + log(" Giving up (too many iterations)\n"); + return; + } } while (!root_mux_rerun.empty()) { @@ -192,9 +196,14 @@ struct OptMuxtreeWorker log_assert(root_enable_muxes.at(mux_idx)); root_mux_rerun.erase(mux_idx); eval_root_mux(mux_idx); + if (glob_abort_cnt == 0) { + log(" Giving up (too many iterations)\n"); + return; + } } log(" Analyzing evaluation results.\n"); + log_assert(glob_abort_cnt > 0); for (auto &mi : mux2info) { @@ -397,10 +406,8 @@ struct OptMuxtreeWorker void eval_mux(knowledge_t &knowledge, int mux_idx, bool do_replace_known, bool do_enable_ports, int abort_count) { - if (glob_abort_cnt == 0) { - log(" Giving up (too many iterations)\n"); + if (glob_abort_cnt == 0) return; - } glob_abort_cnt--; muxinfo_t &muxinfo = mux2info[mux_idx]; @@ -454,6 +461,7 @@ struct OptMuxtreeWorker void eval_root_mux(int mux_idx) { + log_assert(glob_abort_cnt > 0); knowledge_t knowledge; knowledge.known_inactive.resize(GetSize(bit2info)); knowledge.known_active.resize(GetSize(bit2info)); diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc index e8570f0eb..eeb992a3e 100644 --- a/passes/opt/opt_rmdff.cc +++ b/passes/opt/opt_rmdff.cc @@ -260,8 +260,8 @@ delete_dlatch: bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) { - RTLIL::SigSpec sig_d, sig_q, sig_c, sig_r; - RTLIL::Const val_cp, val_rp, val_rv; + RTLIL::SigSpec sig_d, sig_q, sig_c, sig_r, sig_e; + RTLIL::Const val_cp, val_rp, val_rv, val_ep; if (dff->type == "$_FF_") { sig_d = dff->getPort("\\D"); @@ -285,6 +285,16 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) val_rp = RTLIL::Const(dff->type[7] == 'P', 1); val_rv = RTLIL::Const(dff->type[8] == '1', 1); } + else if (dff->type.substr(0,7) == "$_DFFE_" && dff->type.substr(9) == "_" && + (dff->type[7] == 'N' || dff->type[7] == 'P') && + (dff->type[8] == 'N' || dff->type[8] == 'P')) { + sig_d = dff->getPort("\\D"); + sig_q = dff->getPort("\\Q"); + sig_c = dff->getPort("\\C"); + sig_e = dff->getPort("\\E"); + val_cp = RTLIL::Const(dff->type[7] == 'P', 1); + val_ep = RTLIL::Const(dff->type[8] == 'P', 1); + } else if (dff->type == "$ff") { sig_d = dff->getPort("\\D"); sig_q = dff->getPort("\\Q"); @@ -295,6 +305,14 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) sig_c = dff->getPort("\\CLK"); val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1); } + else if (dff->type == "$dffe") { + sig_e = dff->getPort("\\EN"); + sig_d = dff->getPort("\\D"); + sig_q = dff->getPort("\\Q"); + sig_c = dff->getPort("\\CLK"); + val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1); + val_ep = RTLIL::Const(dff->parameters["\\EN_POLARITY"].as_bool(), 1); + } else if (dff->type == "$adff") { sig_d = dff->getPort("\\D"); sig_q = dff->getPort("\\Q"); @@ -337,39 +355,60 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) } } + // If clock is driven by a constant and (i) no reset signal + // (ii) Q has no initial value + // (iii) initial value is same as reset value if (!sig_c.empty() && sig_c.is_fully_const() && (!sig_r.size() || !has_init || val_init == val_rv)) { if (val_rv.bits.size() == 0) val_rv = val_init; + // Q is permanently reset value or initial value mod->connect(sig_q, val_rv); goto delete_dff; } + // If D is fully undefined and reset signal present and (i) Q has no initial value + // (ii) initial value is same as reset value if (sig_d.is_fully_undef() && sig_r.size() && (!has_init || val_init == val_rv)) { + // Q is permanently reset value mod->connect(sig_q, val_rv); goto delete_dff; } + // If D is fully undefined and no reset signal and Q has an initial value if (sig_d.is_fully_undef() && !sig_r.size() && has_init) { + // Q is permanently initial value mod->connect(sig_q, val_init); goto delete_dff; } + // If D is fully constant and (i) no reset signal + // (ii) reset value is same as constant D + // and (a) has no initial value + // (b) initial value same as constant D if (sig_d.is_fully_const() && (!sig_r.size() || val_rv == sig_d.as_const()) && (!has_init || val_init == sig_d.as_const())) { + // Q is permanently D mod->connect(sig_q, sig_d); goto delete_dff; } + // If D input is same as Q output and (i) no reset signal + // (ii) no initial signal + // (iii) initial value is same as reset value if (sig_d == sig_q && (sig_r.empty() || !has_init || val_init == val_rv)) { + // Q is permanently reset value or initial value if (sig_r.size()) mod->connect(sig_q, val_rv); - if (has_init) + else if (has_init) mod->connect(sig_q, val_init); goto delete_dff; } + // If reset signal is present, and is fully constant if (!sig_r.empty() && sig_r.is_fully_const()) { + // If reset value is permanently active or if reset is undefined if (sig_r == val_rp || sig_r.is_fully_undef()) { + // Q is permanently reset value mod->connect(sig_q, val_rv); goto delete_dff; } @@ -389,6 +428,30 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) dff->unsetPort("\\R"); } + // If enable signal is present, and is fully constant + if (!sig_e.empty() && sig_e.is_fully_const()) + { + // If enable value is permanently inactive + if (sig_e != val_ep) { + // Q is permanently initial value + mod->connect(sig_q, val_init); + goto delete_dff; + } + + log("Removing unused enable from %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod)); + + if (dff->type == "$dffe") { + dff->type = "$dff"; + dff->unsetPort("\\EN"); + dff->unsetParam("\\EN_POLARITY"); + return true; + } + + log_assert(dff->type.substr(0,7) == "$_DFFE_"); + dff->type = stringf("$_DFF_%c_", + dff->type[7]); + dff->unsetPort("\\E"); + } + return false; delete_dff: @@ -489,7 +552,8 @@ struct OptRmdffPass : public Pass { if (cell->type.in("$_FF_", "$_DFF_N_", "$_DFF_P_", "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_", "$_DFF_PN0_", "$_DFF_PN1_", "$_DFF_PP0_", "$_DFF_PP1_", - "$ff", "$dff", "$adff")) + "$_DFFE_NN_", "$_DFFE_NP_", "$_DFFE_PN_", "$_DFFE_PP_", + "$ff", "$dff", "$dffe", "$adff")) dff_list.push_back(cell->name); if (cell->type.in("$dlatch", "$_DLATCH_P_", "$_DLATCH_N_")) diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 52245ce3e..1fbc41082 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -29,6 +29,7 @@ PRIVATE_NAMESPACE_BEGIN struct WreduceConfig { pool<IdString> supported_cell_types; + bool keepdc = false; WreduceConfig() { @@ -82,7 +83,7 @@ struct WreduceWorker SigBit ref = sig_a[i]; for (int k = 0; k < GetSize(sig_s); k++) { - if (ref != Sx && sig_b[k*GetSize(sig_a) + i] != Sx && ref != sig_b[k*GetSize(sig_a) + i]) + if ((config->keepdc || (ref != Sx && sig_b[k*GetSize(sig_a) + i] != Sx)) && ref != sig_b[k*GetSize(sig_a) + i]) goto no_match_ab; if (sig_b[k*GetSize(sig_a) + i] != Sx) ref = sig_b[k*GetSize(sig_a) + i]; @@ -180,6 +181,8 @@ struct WreduceWorker } auto info = mi.query(sig_q[i]); + if (info == nullptr) + return; if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) { remove_init_bits.insert(sig_q[i]); sig_d.remove(i); @@ -462,12 +465,10 @@ struct WreduceWorker SigSpec initsig = init_attr_sigmap(w); int width = std::min(GetSize(initval), GetSize(initsig)); for (int i = 0; i < width; i++) { - log_dump(initsig[i], remove_init_bits.count(initsig[i])); if (!remove_init_bits.count(initsig[i])) new_initval[i] = initval[i]; } w->attributes.at("\\init") = new_initval; - log_dump(w->name, initval, new_initval); } } } @@ -495,6 +496,9 @@ struct WreducePass : public Pass { log(" Do not change the width of memory address ports. Use this options in\n"); log(" flows that use the 'memory_memx' pass.\n"); log("\n"); + log(" -keepdc\n"); + log(" Do not optimize explicit don't-care values.\n"); + log("\n"); } void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE { @@ -509,6 +513,10 @@ struct WreducePass : public Pass { opt_memx = true; continue; } + if (args[argidx] == "-keepdc") { + config.keepdc = true; + continue; + } break; } extra_args(args, argidx, design); @@ -531,6 +539,42 @@ struct WreducePass : public Pass { module->connect(sig, Const(0, GetSize(sig))); } } + + if (c->type.in("$div", "$mod", "$pow")) + { + SigSpec A = c->getPort("\\A"); + int original_a_width = GetSize(A); + if (c->getParam("\\A_SIGNED").as_bool()) { + while (GetSize(A) > 1 && A[GetSize(A)-1] == State::S0 && A[GetSize(A)-2] == State::S0) + A.remove(GetSize(A)-1, 1); + } else { + while (GetSize(A) > 0 && A[GetSize(A)-1] == State::S0) + A.remove(GetSize(A)-1, 1); + } + if (original_a_width != GetSize(A)) { + log("Removed top %d bits (of %d) from port A of cell %s.%s (%s).\n", + original_a_width-GetSize(A), original_a_width, log_id(module), log_id(c), log_id(c->type)); + c->setPort("\\A", A); + c->setParam("\\A_WIDTH", GetSize(A)); + } + + SigSpec B = c->getPort("\\B"); + int original_b_width = GetSize(B); + if (c->getParam("\\B_SIGNED").as_bool()) { + while (GetSize(B) > 1 && B[GetSize(B)-1] == State::S0 && B[GetSize(B)-2] == State::S0) + B.remove(GetSize(B)-1, 1); + } else { + while (GetSize(B) > 0 && B[GetSize(B)-1] == State::S0) + B.remove(GetSize(B)-1, 1); + } + if (original_b_width != GetSize(B)) { + log("Removed top %d bits (of %d) from port B of cell %s.%s (%s).\n", + original_b_width-GetSize(B), original_b_width, log_id(module), log_id(c), log_id(c->type)); + c->setPort("\\B", B); + c->setParam("\\B_WIDTH", GetSize(B)); + } + } + if (!opt_memx && c->type.in("$memrd", "$memwr", "$meminit")) { IdString memid = c->getParam("\\MEMID").decode_string(); RTLIL::Memory *mem = module->memories.at(memid); diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore index c9263057e..0ad36ea2c 100644 --- a/passes/pmgen/.gitignore +++ b/passes/pmgen/.gitignore @@ -1 +1,2 @@ /ice40_dsp_pm.h +/peepopt_pm.h diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc index e0609d9ba..7911132db 100644 --- a/passes/pmgen/Makefile.inc +++ b/passes/pmgen/Makefile.inc @@ -1,8 +1,23 @@ OBJS += passes/pmgen/ice40_dsp.o +OBJS += passes/pmgen/peepopt.o + +# -------------------------------------- passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h .SECONDARY: passes/pmgen/ice40_dsp_pm.h passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg - $(P) mkdir -p passes/pmgen && python3 $^ $@ + $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p ice40_dsp $(filter-out $<,$^) + +# -------------------------------------- + +passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h +EXTRA_OBJS += passes/pmgen/peepopt_pm.h +.SECONDARY: passes/pmgen/peepopt_pm.h + +PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul.pmg +PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg + +passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) + $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^) diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md index 7a46558b1..2f0b1fd5a 100644 --- a/passes/pmgen/README.md +++ b/passes/pmgen/README.md @@ -29,19 +29,25 @@ up in any future matches: pm.blacklist(some_cell); -The `.run(callback_function)` method searches for all matches and calls the -callback function for each found match: +The `.run_<pattern_name>(callback_function)` method searches for all matches +for the pattern`<pattern_name>` and calls the callback function for each found +match: - pm.run([&](){ + pm.run_foobar([&](){ log("found matching 'foo' cell: %s\n", log_id(pm.st.foo)); log(" with 'bar' cell: %s\n", log_id(pm.st.bar)); }); The `.pmg` file declares matcher state variables that are accessible via the -`.st.<state_name>` members. (The `.st` member is of type `foobar_pm::state_t`.) +`.st_<pattern_name>.<state_name>` members. (The `.st_<pattern_name>` member is +of type `foobar_pm::state_<pattern_name>_t`.) Similarly the `.pmg` file declares user data variables that become members of -`.ud`, a struct of type `foobar_pm::udata_t`. +`.ud_<pattern_name>`, a struct of type `foobar_pm::udata_<pattern_name>_t`. + +There are four versions of the `run_<pattern_name>()` method: Without callback, +callback without arguments, callback with reference to `pm`, and callback with +reference to `pm.st_<pattern_name>`. The .pmg File Format @@ -52,6 +58,12 @@ lines consist of whitespace-separated tokens. Lines in `.pmg` files starting with `//` are comments. +Declaring a pattern +------------------- + +A `.pmg` file contains one or more patterns. Each pattern starts with a line +with the `pattern` keyword followed by the name of the pattern. + Declaring state variables ------------------------- @@ -66,7 +78,7 @@ State variables are automatically managed by the generated backtracking algorith and saved and restored as needed. They are automatically initialized to the default constructed value of their type -when `.run(callback_function)` is called. +when `.run_<pattern_name>(callback_function)` is called. Declaring udata variables ------------------------- @@ -220,5 +232,5 @@ But in some cases it is more natural to utilize the implicit branch statement: portAB = \B; endcode -There is an implicit `code..endcode` block at the end of each `.pgm` file +There is an implicit `code..endcode` block at the end of each `.pmg` file that just accepts everything that gets all the way there. diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc index 3a054a463..39d033a04 100644 --- a/passes/pmgen/ice40_dsp.cc +++ b/passes/pmgen/ice40_dsp.cc @@ -19,47 +19,50 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" -#include "passes/pmgen/ice40_dsp_pm.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +#include "passes/pmgen/ice40_dsp_pm.h" + void create_ice40_dsp(ice40_dsp_pm &pm) { + auto &st = pm.st_ice40_dsp; + #if 0 log("\n"); - log("ffA: %s\n", log_id(pm.st.ffA, "--")); - log("ffB: %s\n", log_id(pm.st.ffB, "--")); - log("mul: %s\n", log_id(pm.st.mul, "--")); - log("ffY: %s\n", log_id(pm.st.ffY, "--")); - log("addAB: %s\n", log_id(pm.st.addAB, "--")); - log("muxAB: %s\n", log_id(pm.st.muxAB, "--")); - log("ffS: %s\n", log_id(pm.st.ffS, "--")); + log("ffA: %s\n", log_id(st.ffA, "--")); + log("ffB: %s\n", log_id(st.ffB, "--")); + log("mul: %s\n", log_id(st.mul, "--")); + log("ffY: %s\n", log_id(st.ffY, "--")); + log("addAB: %s\n", log_id(st.addAB, "--")); + log("muxAB: %s\n", log_id(st.muxAB, "--")); + log("ffS: %s\n", log_id(st.ffS, "--")); #endif - log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul)); + log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul)); - if (GetSize(pm.st.sigA) > 16) { - log(" input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA)); + if (GetSize(st.sigA) > 16) { + log(" input A (%s) is too large (%d > 16).\n", log_signal(st.sigA), GetSize(st.sigA)); return; } - if (GetSize(pm.st.sigB) > 16) { - log(" input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB)); + if (GetSize(st.sigB) > 16) { + log(" input B (%s) is too large (%d > 16).\n", log_signal(st.sigB), GetSize(st.sigB)); return; } - if (GetSize(pm.st.sigS) > 32) { - log(" accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS)); + if (GetSize(st.sigS) > 32) { + log(" accumulator (%s) is too large (%d > 32).\n", log_signal(st.sigS), GetSize(st.sigS)); return; } - if (GetSize(pm.st.sigY) > 32) { - log(" output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY)); + if (GetSize(st.sigY) > 32) { + log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigY), GetSize(st.sigY)); return; } - bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool(); + bool mul_signed = st.mul->getParam("\\A_SIGNED").as_bool(); if (mul_signed) { log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n"); @@ -69,21 +72,21 @@ void create_ice40_dsp(ice40_dsp_pm &pm) log(" replacing $mul with SB_MAC16 cell.\n"); Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16"); - pm.module->swap_names(cell, pm.st.mul); + pm.module->swap_names(cell, st.mul); // SB_MAC16 Input Interface - SigSpec A = pm.st.sigA; + SigSpec A = st.sigA; A.extend_u0(16, mul_signed); - SigSpec B = pm.st.sigB; + SigSpec B = st.sigB; B.extend_u0(16, mul_signed); SigSpec CD; - if (pm.st.muxA) - CD = pm.st.muxA->getPort("\\B"); - if (pm.st.muxB) - CD = pm.st.muxB->getPort("\\A"); + if (st.muxA) + CD = st.muxA->getPort("\\B"); + if (st.muxB) + CD = st.muxB->getPort("\\A"); CD.extend_u0(32, mul_signed); cell->setPort("\\A", A); @@ -91,8 +94,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm) cell->setPort("\\C", CD.extract(0, 16)); cell->setPort("\\D", CD.extract(16, 16)); - cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0); - cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0); + cell->setParam("\\A_REG", st.ffA ? State::S1 : State::S0); + cell->setParam("\\B_REG", st.ffB ? State::S1 : State::S0); cell->setPort("\\AHOLD", State::S0); cell->setPort("\\BHOLD", State::S0); @@ -102,25 +105,25 @@ void create_ice40_dsp(ice40_dsp_pm &pm) cell->setPort("\\IRSTTOP", State::S0); cell->setPort("\\IRSTBOT", State::S0); - if (pm.st.clock_vld) + if (st.clock_vld) { - cell->setPort("\\CLK", pm.st.clock); + cell->setPort("\\CLK", st.clock); cell->setPort("\\CE", State::S1); - cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1); + cell->setParam("\\NEG_TRIGGER", st.clock_pol ? State::S0 : State::S1); - log(" clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge"); + log(" clock: %s (%s)", log_signal(st.clock), st.clock_pol ? "posedge" : "negedge"); - if (pm.st.ffA) - log(" ffA:%s", log_id(pm.st.ffA)); + if (st.ffA) + log(" ffA:%s", log_id(st.ffA)); - if (pm.st.ffB) - log(" ffB:%s", log_id(pm.st.ffB)); + if (st.ffB) + log(" ffB:%s", log_id(st.ffB)); - if (pm.st.ffY) - log(" ffY:%s", log_id(pm.st.ffY)); + if (st.ffY) + log(" ffY:%s", log_id(st.ffY)); - if (pm.st.ffS) - log(" ffS:%s", log_id(pm.st.ffS)); + if (st.ffS) + log(" ffS:%s", log_id(st.ffS)); log("\n"); } @@ -144,16 +147,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm) // SB_MAC16 Output Interface - SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY; + SigSpec O = st.ffS ? st.sigS : st.sigY; if (GetSize(O) < 32) O.append(pm.module->addWire(NEW_ID, 32-GetSize(O))); cell->setPort("\\O", O); - if (pm.st.addAB) { - log(" accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type)); - cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1); - cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1); + if (st.addAB) { + log(" accumulator %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type)); + cell->setPort("\\ADDSUBTOP", st.addAB->type == "$add" ? State::S0 : State::S1); + cell->setPort("\\ADDSUBBOT", st.addAB->type == "$add" ? State::S0 : State::S1); } else { cell->setPort("\\ADDSUBTOP", State::S0); cell->setPort("\\ADDSUBBOT", State::S0); @@ -166,10 +169,10 @@ void create_ice40_dsp(ice40_dsp_pm &pm) cell->setPort("\\OHOLDBOT", State::S0); SigSpec acc_reset = State::S0; - if (pm.st.muxA) - acc_reset = pm.st.muxA->getPort("\\S"); - if (pm.st.muxB) - acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S")); + if (st.muxA) + acc_reset = st.muxA->getPort("\\S"); + if (st.muxB) + acc_reset = pm.module->Not(NEW_ID, st.muxB->getPort("\\S")); cell->setPort("\\OLOADTOP", acc_reset); cell->setPort("\\OLOADBOT", acc_reset); @@ -179,17 +182,17 @@ void create_ice40_dsp(ice40_dsp_pm &pm) cell->setParam("\\C_REG", State::S0); cell->setParam("\\D_REG", State::S0); - cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); - cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); - cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0); + cell->setParam("\\TOP_8x8_MULT_REG", st.ffY ? State::S1 : State::S0); + cell->setParam("\\BOT_8x8_MULT_REG", st.ffY ? State::S1 : State::S0); + cell->setParam("\\PIPELINE_16x16_MULT_REG1", st.ffY ? State::S1 : State::S0); cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0); - cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2)); + cell->setParam("\\TOPOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2)); cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2)); cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0); cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2)); - cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2)); + cell->setParam("\\BOTOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2)); cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2)); cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0); cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2)); @@ -198,9 +201,9 @@ void create_ice40_dsp(ice40_dsp_pm &pm) cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0); cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0); - pm.autoremove(pm.st.mul); - pm.autoremove(pm.st.ffY); - pm.autoremove(pm.st.ffS); + pm.autoremove(st.mul); + pm.autoremove(st.ffY); + pm.autoremove(st.ffS); } struct Ice40DspPass : public Pass { @@ -230,7 +233,7 @@ struct Ice40DspPass : public Pass { extra_args(args, argidx, design); for (auto module : design->selected_modules()) - ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp); + ice40_dsp_pm(module, module->selected_cells()).run_ice40_dsp(create_ice40_dsp); } } Ice40DspPass; diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg index 96c62e313..1f3590d4e 100644 --- a/passes/pmgen/ice40_dsp.pmg +++ b/passes/pmgen/ice40_dsp.pmg @@ -1,3 +1,5 @@ +pattern ice40_dsp + state <SigBit> clock state <bool> clock_pol clock_vld state <SigSpec> sigA sigB sigY sigS diff --git a/passes/pmgen/peepopt.cc b/passes/pmgen/peepopt.cc new file mode 100644 index 000000000..e7f95cf85 --- /dev/null +++ b/passes/pmgen/peepopt.cc @@ -0,0 +1,68 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * 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 + +bool did_something; + +#include "passes/pmgen/peepopt_pm.h" + +struct PeepoptPass : public Pass { + PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" peepopt [options] [selection]\n"); + log("\n"); + log("This pass applies a collection of peephole optimizers to the current design.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-singleton") { + // singleton_mode = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) { + did_something = true; + while (did_something) { + did_something = false; + peepopt_pm pm(module, module->selected_cells()); + pm.run_shiftmul(); + pm.run_muldiv(); + } + } + } +} PeepoptPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/pmgen/peepopt_muldiv.pmg b/passes/pmgen/peepopt_muldiv.pmg new file mode 100644 index 000000000..06c275834 --- /dev/null +++ b/passes/pmgen/peepopt_muldiv.pmg @@ -0,0 +1,36 @@ +pattern muldiv + +state <SigSpec> t x y + +match mul + select mul->type == $mul + select GetSize(port(mul, \A)) + GetSize(port(mul, \B)) <= GetSize(port(mul, \Y)) +endmatch + +code t x y + t = port(mul, \Y); + x = port(mul, \A); + y = port(mul, \B); + branch; + std::swap(x, y); +endcode + +match div + select div->type.in($div) + index <SigSpec> port(div, \A) === t + index <SigSpec> port(div, \B) === x +endmatch + +code + SigSpec div_y = port(div, \Y); + SigSpec val_y = y; + + if (GetSize(div_y) != GetSize(val_y)) + val_y.extend_u0(GetSize(div_y), param(div, \A_SIGNED).as_bool()); + + did_something = true; + log("muldiv pattern in %s: mul=%s, div=%s\n", log_id(module), log_id(mul), log_id(div)); + module->connect(div_y, val_y); + autoremove(div); + reject; +endcode diff --git a/passes/pmgen/peepopt_shiftmul.pmg b/passes/pmgen/peepopt_shiftmul.pmg new file mode 100644 index 000000000..6adab4e5f --- /dev/null +++ b/passes/pmgen/peepopt_shiftmul.pmg @@ -0,0 +1,94 @@ +pattern shiftmul +// +// Optimize mul+shift pairs that result from expressions such as foo[s*W+:W] +// + +state <SigSpec> shamt + +match shift + select shift->type.in($shift, $shiftx, $shr) +endmatch + +code shamt + shamt = port(shift, \B); + if (shamt.empty()) + reject; + if (shamt[GetSize(shamt)-1] == State::S0) { + do { + shamt.remove(GetSize(shamt)-1); + if (shamt.empty()) + reject; + } while (shamt[GetSize(shamt)-1] == State::S0); + } else + if (shift->type.in($shift, $shiftx) && param(shift, \B_SIGNED).as_bool()) { + reject; + } + if (GetSize(shamt) > 20) + reject; +endcode + +match mul + select mul->type.in($mul) + select port(mul, \A).is_fully_const() || port(mul, \B).is_fully_const() + index <SigSpec> port(mul, \Y) === shamt +endmatch + +code + IdString const_factor_port = port(mul, \A).is_fully_const() ? \A : \B; + IdString const_factor_signed = const_factor_port == \A ? \A_SIGNED : \B_SIGNED; + Const const_factor_cnst = port(mul, const_factor_port).as_const(); + int const_factor = const_factor_cnst.as_int(); + + if (GetSize(const_factor_cnst) == 0) + reject; + + if (const_factor_cnst.bits[GetSize(const_factor_cnst)-1] != State::S0 && + param(mul, const_factor_signed).as_bool()) + reject; + + if (GetSize(const_factor_cnst) > 20) + reject; + + if (GetSize(port(shift, \Y)) > const_factor) + reject; + + int factor_bits = ceil_log2(const_factor); + SigSpec mul_din = port(mul, const_factor_port == \A ? \B : \A); + + if (GetSize(shamt) < factor_bits+GetSize(mul_din)) + reject; + + did_something = true; + log("shiftmul pattern in %s: shift=%s, mul=%s\n", log_id(module), log_id(shift), log_id(mul)); + + int new_const_factor = 1 << factor_bits; + SigSpec padding(State::Sx, new_const_factor-const_factor); + SigSpec old_a = port(shift, \A), new_a; + int trunc = 0; + + if (GetSize(old_a) % const_factor != 0) { + trunc = const_factor - GetSize(old_a) % const_factor; + old_a.append(SigSpec(State::Sx, trunc)); + } + + for (int i = 0; i*const_factor < GetSize(old_a); i++) { + SigSpec slice = old_a.extract(i*const_factor, const_factor); + new_a.append(slice); + new_a.append(padding); + } + + if (trunc > 0) + new_a.remove(GetSize(new_a)-trunc, trunc); + + SigSpec new_b = {mul_din, SigSpec(State::S0, factor_bits)}; + if (param(shift, \B_SIGNED).as_bool()) + new_b.append(State::S0); + + shift->setPort(\A, new_a); + shift->setParam(\A_WIDTH, GetSize(new_a)); + shift->setPort(\B, new_b); + shift->setParam(\B_WIDTH, GetSize(new_b)); + + blacklist(shift); + reject; +endcode diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py index d9747b065..81052afce 100644 --- a/passes/pmgen/pmgen.py +++ b/passes/pmgen/pmgen.py @@ -3,15 +3,42 @@ import re import sys import pprint +import getopt pp = pprint.PrettyPrinter(indent=4) -pmgfile = sys.argv[1] -assert pmgfile.endswith(".pmg") -prefix = pmgfile[0:-4] -prefix = prefix.split('/')[-1] -outfile = sys.argv[2] - +prefix = None +pmgfiles = list() +outfile = None +debug = False +genhdr = False + +opts, args = getopt.getopt(sys.argv[1:], "p:o:dg") + +for o, a in opts: + if o == "-p": + prefix = a + elif o == "-o": + outfile = a + elif o == "-d": + debug = True + elif o == "-g": + genhdr = True + +if outfile is None: + outfile = "/dev/stdout" + +for a in args: + assert a.endswith(".pmg") + if prefix is None and len(args) == 1: + prefix = a[0:-4] + prefix = prefix.split('/')[-1] + pmgfiles.append(a) + +assert prefix is not None + +current_pattern = None +patterns = dict() state_types = dict() udata_types = dict() blocks = list() @@ -77,7 +104,8 @@ def rewrite_cpp(s): return "".join(t) -with open(pmgfile, "r") as f: +def process_pmgfile(f): + global current_pattern while True: line = f.readline() if line == "": break @@ -87,14 +115,31 @@ with open(pmgfile, "r") as f: if len(cmd) == 0 or cmd[0].startswith("//"): continue cmd = cmd[0] + if cmd == "pattern": + if current_pattern is not None: + block = dict() + block["type"] = "final" + block["pattern"] = current_pattern + blocks.append(block) + line = line.split() + assert len(line) == 2 + assert line[1] not in patterns + current_pattern = line[1] + patterns[current_pattern] = len(blocks) + state_types[current_pattern] = dict() + udata_types[current_pattern] = dict() + continue + + assert current_pattern is not None + if cmd == "state": m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line) assert m type_str = m.group(1) states_str = m.group(2) for s in re.split(r"\s+", states_str): - assert s not in state_types - state_types[s] = type_str + assert s not in state_types[current_pattern] + state_types[current_pattern][s] = type_str continue if cmd == "udata": @@ -103,19 +148,20 @@ with open(pmgfile, "r") as f: type_str = m.group(1) udatas_str = m.group(2) for s in re.split(r"\s+", udatas_str): - assert s not in udata_types - udata_types[s] = type_str + assert s not in udata_types[current_pattern] + udata_types[current_pattern][s] = type_str continue if cmd == "match": block = dict() block["type"] = "match" + block["pattern"] = current_pattern line = line.split() assert len(line) == 2 - assert line[1] not in state_types + assert line[1] not in state_types[current_pattern] block["cell"] = line[1] - state_types[line[1]] = "Cell*"; + state_types[current_pattern][line[1]] = "Cell*"; block["if"] = list() block["select"] = list() @@ -158,15 +204,18 @@ with open(pmgfile, "r") as f: assert False blocks.append(block) + continue if cmd == "code": block = dict() block["type"] = "code" + block["pattern"] = current_pattern + block["code"] = list() block["states"] = set() for s in line.split()[1:]: - assert s in state_types + assert s in state_types[current_pattern] block["states"].add(s) while True: @@ -179,18 +228,37 @@ with open(pmgfile, "r") as f: block["code"].append(rewrite_cpp(l.rstrip())) blocks.append(block) + continue -with open(outfile, "w") as f: - print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f) - print("", file=f) + assert False - print("#include \"kernel/yosys.h\"", file=f) - print("#include \"kernel/sigtools.h\"", file=f) - print("", file=f) +for fn in pmgfiles: + with open(fn, "r") as f: + process_pmgfile(f) + +if current_pattern is not None: + block = dict() + block["type"] = "final" + block["pattern"] = current_pattern + blocks.append(block) + +current_pattern = None + +if debug: + pp.pprint(blocks) - print("YOSYS_NAMESPACE_BEGIN", file=f) +with open(outfile, "w") as f: + for fn in pmgfiles: + print("// Generated by pmgen.py from {}".format(fn), file=f) print("", file=f) + if genhdr: + print("#include \"kernel/yosys.h\"", file=f) + print("#include \"kernel/sigtools.h\"", file=f) + print("", file=f) + print("YOSYS_NAMESPACE_BEGIN", file=f) + print("", file=f) + print("struct {}_pm {{".format(prefix), file=f) print(" Module *module;", file=f) print(" SigMap sigmap;", file=f) @@ -212,17 +280,19 @@ with open(outfile, "w") as f: print(" int rollback;", file=f) print("", file=f) - print(" struct state_t {", file=f) - for s, t in sorted(state_types.items()): - print(" {} {};".format(t, s), file=f) - print(" } st;", file=f) - print("", file=f) + for current_pattern in sorted(patterns.keys()): + print(" struct state_{}_t {{".format(current_pattern), file=f) + for s, t in sorted(state_types[current_pattern].items()): + print(" {} {};".format(t, s), file=f) + print(" }} st_{};".format(current_pattern), file=f) + print("", file=f) - print(" struct udata_t {", file=f) - for s, t in sorted(udata_types.items()): - print(" {} {};".format(t, s), file=f) - print(" } ud;", file=f) - print("", file=f) + print(" struct udata_{}_t {{".format(current_pattern), file=f) + for s, t in sorted(udata_types[current_pattern].items()): + print(" {} {};".format(t, s), file=f) + print(" }} ud_{};".format(current_pattern), file=f) + print("", file=f) + current_pattern = None for v, n in sorted(ids.items()): if n[0] == "\\": @@ -258,20 +328,24 @@ with open(outfile, "w") as f: print(" }", file=f) print("", file=f) - print(" void check_blacklist() {", file=f) - print(" if (!blacklist_dirty)", file=f) - print(" return;", file=f) - print(" blacklist_dirty = false;", file=f) - for index in range(len(blocks)): - block = blocks[index] - if block["type"] == "match": - print(" if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f) - print(" rollback = {};".format(index+1), file=f) - print(" return;", file=f) - print(" }", file=f) - print(" rollback = 0;", file=f) - print(" }", file=f) - print("", file=f) + for current_pattern in sorted(patterns.keys()): + print(" void check_blacklist_{}() {{".format(current_pattern), file=f) + print(" if (!blacklist_dirty)", file=f) + print(" return;", file=f) + print(" blacklist_dirty = false;", file=f) + for index in range(len(blocks)): + block = blocks[index] + if block["pattern"] != current_pattern: + continue + if block["type"] == "match": + print(" if (st_{}.{} != nullptr && blacklist_cells.count(st_{}.{})) {{".format(current_pattern, block["cell"], current_pattern, block["cell"]), file=f) + print(" rollback = {};".format(index+1), file=f) + print(" return;", file=f) + print(" }", file=f) + print(" rollback = 0;", file=f) + print(" }", file=f) + print("", file=f) + current_pattern = None print(" SigSpec port(Cell *cell, IdString portname) {", file=f) print(" return sigmap(cell->getPort(portname));", file=f) @@ -294,11 +368,13 @@ with open(outfile, "w") as f: print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f) print(" module(module), sigmap(module) {", file=f) - for s, t in sorted(udata_types.items()): - if t.endswith("*"): - print(" ud.{} = nullptr;".format(s), file=f) - else: - print(" ud.{} = {}();".format(s, t), file=f) + for current_pattern in sorted(patterns.keys()): + for s, t in sorted(udata_types[current_pattern].items()): + if t.endswith("*"): + print(" ud_{}.{} = nullptr;".format(current_pattern,s), file=f) + else: + print(" ud_{}.{} = {}();".format(current_pattern, s, t), file=f) + current_pattern = None print(" for (auto cell : module->cells()) {", file=f) print(" for (auto &conn : cell->connections())", file=f) print(" add_siguser(conn.second, cell);", file=f) @@ -328,34 +404,52 @@ with open(outfile, "w") as f: print(" }", file=f) print("", file=f) - print(" void run(std::function<void()> on_accept_f) {", file=f) - print(" on_accept = on_accept_f;", file=f) - print(" rollback = 0;", file=f) - print(" blacklist_dirty = false;", file=f) - for s, t in sorted(state_types.items()): - if t.endswith("*"): - print(" st.{} = nullptr;".format(s), file=f) - else: - print(" st.{} = {}();".format(s, t), file=f) - print(" block_0();", file=f) - print(" }", file=f) - print("", file=f) - - print(" void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f) - print(" run([&](){on_accept_f(*this);});", file=f) - print(" }", file=f) - print("", file=f) + for current_pattern in sorted(patterns.keys()): + print(" void run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f) + print(" on_accept = on_accept_f;", file=f) + print(" rollback = 0;", file=f) + print(" blacklist_dirty = false;", file=f) + for s, t in sorted(state_types[current_pattern].items()): + if t.endswith("*"): + print(" st_{}.{} = nullptr;".format(current_pattern, s), file=f) + else: + print(" st_{}.{} = {}();".format(current_pattern, s, t), file=f) + print(" block_{}();".format(patterns[current_pattern]), file=f) + print(" }", file=f) + print("", file=f) + print(" void run_{}(std::function<void({}_pm&)> on_accept_f) {{".format(current_pattern, prefix), file=f) + print(" run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f) + print(" }", file=f) + print("", file=f) + print(" void run_{}(std::function<void(state_{}_t&)> on_accept_f) {{".format(current_pattern, current_pattern), file=f) + print(" run_{}([&](){{on_accept_f(st_{});}});".format(current_pattern, current_pattern), file=f) + print(" }", file=f) + print("", file=f) + print(" void run_{}() {{".format(current_pattern), file=f) + print(" run_{}([](){{}});".format(current_pattern, current_pattern), file=f) + print(" }", file=f) + print("", file=f) + current_pattern = None for index in range(len(blocks)): block = blocks[index] print(" void block_{}() {{".format(index), file=f) + current_pattern = block["pattern"] + + if block["type"] == "final": + print(" on_accept();", file=f) + print(" check_blacklist_{}();".format(current_pattern), file=f) + print(" }", file=f) + if index+1 != len(blocks): + print("", file=f) + continue const_st = set() nonconst_st = set() restore_st = set() - for i in range(index): + for i in range(patterns[current_pattern], index): if blocks[i]["type"] == "code": for s in blocks[i]["states"]: const_st.add(s) @@ -378,27 +472,27 @@ with open(outfile, "w") as f: assert False for s in sorted(const_st): - t = state_types[s] + t = state_types[current_pattern][s] if t.endswith("*"): - print(" {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) + print(" {} const &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f) else: - print(" const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) + print(" const {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f) for s in sorted(nonconst_st): - t = state_types[s] - print(" {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) + t = state_types[current_pattern][s] + print(" {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f) if len(restore_st): print("", file=f) for s in sorted(restore_st): - t = state_types[s] + t = state_types[current_pattern][s] print(" {} backup_{} = {};".format(t, s, s), file=f) if block["type"] == "code": print("", file=f) print(" do {", file=f) - print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f) - print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f) + print("#define reject do {{ check_blacklist_{}(); goto rollback_label; }} while(0)".format(current_pattern), file=f) + print("#define accept do {{ on_accept(); check_blacklist_{}(); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f) print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) for line in block["code"]: @@ -417,11 +511,11 @@ with open(outfile, "w") as f: if len(restore_st) or len(nonconst_st): print("", file=f) for s in sorted(restore_st): - t = state_types[s] + t = state_types[current_pattern][s] print(" {} = backup_{};".format(s, s), file=f) for s in sorted(nonconst_st): if s not in restore_st: - t = state_types[s] + t = state_types[current_pattern][s] if t.endswith("*"): print(" {} = nullptr;".format(s), file=f) else: @@ -470,17 +564,12 @@ with open(outfile, "w") as f: else: assert False - + current_pattern = None print(" }", file=f) print("", file=f) - print(" void block_{}() {{".format(len(blocks)), file=f) - print(" on_accept();", file=f) - print(" check_blacklist();", file=f) - print(" }", file=f) print("};", file=f) - print("", file=f) - print("YOSYS_NAMESPACE_END", file=f) - -# pp.pprint(blocks) + if genhdr: + print("", file=f) + print("YOSYS_NAMESPACE_END", file=f) diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc index 3add9a9eb..50ab38063 100644 --- a/passes/sat/expose.cc +++ b/passes/sat/expose.cc @@ -508,7 +508,7 @@ struct ExposePass : public Pass { } for (auto &conn : module->connections_) - conn.first = out_to_in_map(sigmap(conn.first)); + conn.first = out_to_in_map(conn.first); } if (flag_cut) diff --git a/passes/sat/fmcombine.cc b/passes/sat/fmcombine.cc index cd75ca860..00c098542 100644 --- a/passes/sat/fmcombine.cc +++ b/passes/sat/fmcombine.cc @@ -26,6 +26,8 @@ PRIVATE_NAMESPACE_BEGIN struct opts_t { + bool initeq = false; + bool anyeq = false; bool fwd = false; bool bwd = false; bool nop = false; @@ -56,7 +58,7 @@ struct FmcombineWorker return newsig; } - void import_prim_cell(Cell *cell, const string &suffix) + Cell *import_prim_cell(Cell *cell, const string &suffix) { Cell *c = module->addCell(cell->name.str() + suffix, cell->type); c->parameters = cell->parameters; @@ -64,6 +66,8 @@ struct FmcombineWorker for (auto &conn : cell->connections()) c->setPort(conn.first, import_sig(conn.second, suffix)); + + return c; } void import_hier_cell(Cell *cell) @@ -102,8 +106,24 @@ struct FmcombineWorker for (auto cell : original->cells()) { if (design->module(cell->type) == nullptr) { - import_prim_cell(cell, "_gold"); - import_prim_cell(cell, "_gate"); + if (opts.anyeq && cell->type.in("$anyseq", "$anyconst")) { + Cell *gold = import_prim_cell(cell, "_gold"); + for (auto &conn : cell->connections()) + module->connect(import_sig(conn.second, "_gate"), gold->getPort(conn.first)); + } else { + Cell *gold = import_prim_cell(cell, "_gold"); + Cell *gate = import_prim_cell(cell, "_gate"); + if (opts.initeq) { + if (cell->type.in("$ff", "$dff", "$dffe", + "$dffsr", "$adff", "$dlatch", "$dlatchsr")) { + SigSpec gold_q = gold->getPort("\\Q"); + SigSpec gate_q = gate->getPort("\\Q"); + SigSpec en = module->Initstate(NEW_ID); + SigSpec eq = module->Eq(NEW_ID, gold_q, gate_q); + module->addAssume(NEW_ID, eq, en); + } + } + } } else { import_hier_cell(cell); } @@ -229,6 +249,13 @@ struct FmcombinePass : public Pass { log("This is useful for formal test benches that check what differences in behavior\n"); log("a slight difference in input causes in a module.\n"); log("\n"); + log(" -initeq\n"); + log(" Insert assumptions that initially all FFs in both circuits have the\n"); + log(" same initial values.\n"); + log("\n"); + log(" -anyeq\n"); + log(" Do not duplicate $anyseq/$anyconst cells.\n"); + log("\n"); log(" -fwd\n"); log(" Insert forward hint assumptions into the combined module.\n"); log("\n"); @@ -261,6 +288,14 @@ struct FmcombinePass : public Pass { // filename = args[++argidx]; // continue; // } + if (args[argidx] == "-initeq") { + opts.initeq = true; + continue; + } + if (args[argidx] == "-anyeq") { + opts.anyeq = true; + continue; + } if (args[argidx] == "-fwd") { opts.fwd = true; continue; @@ -297,7 +332,7 @@ struct FmcombinePass : public Pass { gate_cell = module->cell(gate_name); if (gate_cell == nullptr) - log_cmd_error("Gold cell %s not found in module %s.\n", log_id(gate_name), log_id(module)); + log_cmd_error("Gate cell %s not found in module %s.\n", log_id(gate_name), log_id(module)); } else { @@ -316,7 +351,7 @@ struct FmcombinePass : public Pass { if (!gold_cell->parameters.empty()) log_cmd_error("Gold cell has unresolved instance parameters.\n"); if (!gate_cell->parameters.empty()) - log_cmd_error("Gold cell has unresolved instance parameters.\n"); + log_cmd_error("Gate cell has unresolved instance parameters.\n"); FmcombineWorker worker(design, gold_cell->type, opts); worker.generate(); diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index 695a03e15..cbba738f0 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -1169,6 +1169,7 @@ struct SatPass : public Pass { if (args[argidx] == "-tempinduct-def") { tempinduct = true; tempinduct_def = true; + enable_undef = true; continue; } if (args[argidx] == "-tempinduct-baseonly") { diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index 53e248adf..4c3022c70 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -88,6 +88,8 @@ struct SimInstance SimInstance(SimShared *shared, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) : shared(shared), module(module), instance(instance), parent(parent), sigmap(module) { + log_assert(module); + if (parent) { log_assert(parent->children.count(instance) == 0); parent->children[instance] = this; @@ -848,6 +850,9 @@ struct SimPass : public Pass { if (design->full_selection()) { top_mod = design->top_module(); + + if (!top_mod) + log_cmd_error("Design has no top module, use the 'hierarchy' command to specify one.\n"); } else { auto mods = design->selected_whole_modules(); if (GetSize(mods) != 1) diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 547115459..5b19d84fb 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -330,20 +330,33 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr) { std::string abc_sname = abc_name.substr(1); - if (abc_sname.substr(0, 5) == "ys__n") { - bool inv = abc_sname.back() == 'v'; - if (inv) abc_sname.pop_back(); + bool isnew = false; + if (abc_sname.substr(0, 4) == "new_") + { + abc_sname.erase(0, 4); + isnew = true; + } + if (abc_sname.substr(0, 5) == "ys__n") + { abc_sname.erase(0, 5); - if (abc_sname.find_last_not_of("012345689") == std::string::npos) { + if (std::isdigit(abc_sname.at(0))) + { int sid = std::stoi(abc_sname); - for (auto sig : signal_list) { - if (sig.id == sid && sig.bit.wire != nullptr) { + size_t postfix_start = abc_sname.find_first_not_of("0123456789"); + std::string postfix = postfix_start != std::string::npos ? abc_sname.substr(postfix_start) : ""; + + if (sid < GetSize(signal_list)) + { + auto sig = signal_list.at(sid); + if (sig.bit.wire != nullptr) + { std::stringstream sstr; sstr << "$abc$" << map_autoidx << "$" << sig.bit.wire->name.substr(1); if (sig.bit.wire->width != 1) sstr << "[" << sig.bit.offset << "]"; - if (inv) - sstr << "_inv"; + if (isnew) + sstr << "_new"; + sstr << postfix; if (orig_wire != nullptr) *orig_wire = sig.bit.wire; return sstr.str(); diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc index 48390488e..0ad33dc0e 100644 --- a/passes/techmap/dffinit.cc +++ b/passes/techmap/dffinit.cc @@ -102,7 +102,8 @@ struct DffinitPass : public Pass { if (wire->attributes.count("\\init")) { Const value = wire->attributes.at("\\init"); for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) - init_bits[sigmap(SigBit(wire, i))] = value[i]; + if (value[i] != State::Sx) + init_bits[sigmap(SigBit(wire, i))] = value[i]; } if (wire->port_output) for (auto bit : sigmap(wire)) diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc index 0b7931e48..f5892a60e 100644 --- a/passes/techmap/flowmap.cc +++ b/passes/techmap/flowmap.cc @@ -397,7 +397,6 @@ struct FlowGraph pool<RTLIL::SigBit> x, xi; NodePrime source_prime = {source, true}; - NodePrime sink_prime = {sink, false}; pool<NodePrime> visited; vector<NodePrime> worklist = {source_prime}; while (!worklist.empty()) @@ -1382,7 +1381,8 @@ struct FlowmapWorker vector<RTLIL::SigBit> input_nodes(lut_edges_bw[node].begin(), lut_edges_bw[node].end()); RTLIL::Const lut_table(State::Sx, max(1 << input_nodes.size(), 1 << minlut)); - for (unsigned i = 0; i < (1 << input_nodes.size()); i++) + unsigned const mask = 1 << input_nodes.size(); + for (unsigned i = 0; i < mask; i++) { ce.push(); for (size_t n = 0; n < input_nodes.size(); n++) diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 991cc4498..349ccc115 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -94,7 +94,7 @@ int LibertyParser::lexer(std::string &str) // search for identifiers, numbers, plus or minus. if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.') { - str = c; + str = static_cast<char>(c); while (1) { c = f.get(); if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.') diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc index 12da9ed0c..32102436d 100644 --- a/passes/techmap/muxcover.cc +++ b/passes/techmap/muxcover.cc @@ -58,12 +58,21 @@ struct MuxcoverWorker bool use_mux16; bool nodecode; + int cost_mux2; + int cost_mux4; + int cost_mux8; + int cost_mux16; + MuxcoverWorker(Module *module) : module(module), sigmap(module) { use_mux4 = false; use_mux8 = false; use_mux16 = false; nodecode = false; + cost_mux2 = COST_MUX2; + cost_mux4 = COST_MUX4; + cost_mux8 = COST_MUX8; + cost_mux16 = COST_MUX16; decode_mux_counter = 0; } @@ -157,7 +166,7 @@ struct MuxcoverWorker if (std::get<2>(entry)) return 0; - return COST_MUX2 / GetSize(std::get<1>(entry)); + return cost_mux2 / GetSize(std::get<1>(entry)); } void implement_decode_mux(SigBit ctrl_bit) @@ -209,7 +218,7 @@ struct MuxcoverWorker mux.inputs.push_back(B); mux.selects.push_back(S1); - mux.cost += COST_MUX2; + mux.cost += cost_mux2; mux.cost += find_best_cover(tree, A); mux.cost += find_best_cover(tree, B); @@ -247,7 +256,7 @@ struct MuxcoverWorker mux.selects.push_back(S1); mux.selects.push_back(T1); - mux.cost += COST_MUX4; + mux.cost += cost_mux4; mux.cost += find_best_cover(tree, A); mux.cost += find_best_cover(tree, B); mux.cost += find_best_cover(tree, C); @@ -310,7 +319,7 @@ struct MuxcoverWorker mux.selects.push_back(T1); mux.selects.push_back(U1); - mux.cost += COST_MUX8; + mux.cost += cost_mux8; mux.cost += find_best_cover(tree, A); mux.cost += find_best_cover(tree, B); mux.cost += find_best_cover(tree, C); @@ -414,7 +423,7 @@ struct MuxcoverWorker mux.selects.push_back(U1); mux.selects.push_back(V1); - mux.cost += COST_MUX16; + mux.cost += cost_mux16; mux.cost += find_best_cover(tree, A); mux.cost += find_best_cover(tree, B); mux.cost += find_best_cover(tree, C); @@ -569,9 +578,11 @@ struct MuxcoverPass : public Pass { log("\n"); log("Cover trees of $_MUX_ cells with $_MUX{4,8,16}_ cells\n"); log("\n"); - log(" -mux4, -mux8, -mux16\n"); - log(" Use the specified types of MUXes. If none of those options are used,\n"); - log(" the effect is the same as if all of them where used.\n"); + log(" -mux4[=cost], -mux8[=cost], -mux16[=cost]\n"); + log(" Use the specified types of MUXes (with optional integer costs). If none\n"); + log(" of these options are given, the effect is the same as if all of them are.\n"); + log(" Default costs: $_MUX_ = %d, $_MUX4_ = %d,\n", COST_MUX2, COST_MUX4); + log(" $_MUX8_ = %d, $_MUX16_ = %d\n", COST_MUX8, COST_MUX16); log("\n"); log(" -nodecode\n"); log(" Do not insert decoder logic. This reduces the number of possible\n"); @@ -587,23 +598,39 @@ struct MuxcoverPass : public Pass { bool use_mux8 = false; bool use_mux16 = false; bool nodecode = false; + int cost_mux4 = COST_MUX4; + int cost_mux8 = COST_MUX8; + int cost_mux16 = COST_MUX16; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-mux4") { + const auto &arg = args[argidx]; + if (arg.size() >= 5 && arg.substr(0,5) == "-mux4") { use_mux4 = true; + if (arg.size() > 5) { + if (arg[5] != '=') break; + cost_mux4 = atoi(arg.substr(5).c_str()); + } continue; } - if (args[argidx] == "-mux8") { + if (arg.size() >= 5 && arg.substr(0,5) == "-mux8") { use_mux8 = true; + if (arg.size() > 5) { + if (arg[5] != '=') break; + cost_mux8 = atoi(arg.substr(5).c_str()); + } continue; } - if (args[argidx] == "-mux16") { + if (arg.size() >= 6 && arg.substr(0,6) == "-mux16") { use_mux16 = true; + if (arg.size() > 6) { + if (arg[6] != '=') break; + cost_mux16 = atoi(arg.substr(6).c_str()); + } continue; } - if (args[argidx] == "-nodecode") { + if (arg == "-nodecode") { nodecode = true; continue; } @@ -623,6 +650,9 @@ struct MuxcoverPass : public Pass { worker.use_mux4 = use_mux4; worker.use_mux8 = use_mux8; worker.use_mux16 = use_mux16; + worker.cost_mux4 = cost_mux4; + worker.cost_mux8 = cost_mux8; + worker.cost_mux16 = cost_mux16; worker.nodecode = nodecode; worker.run(); } diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc index a541b33be..21dfe9619 100644 --- a/passes/techmap/shregmap.cc +++ b/passes/techmap/shregmap.cc @@ -178,7 +178,17 @@ struct ShregmapTechXilinx7 : ShregmapTech // Only map if $shiftx exclusively covers the shift register if (shiftx->type == "$shiftx") { - if (GetSize(taps) != shiftx->getParam("\\A_WIDTH").as_int()) + if (GetSize(taps) > shiftx->getParam("\\A_WIDTH").as_int()) + return false; + // Due to padding the most significant bits of A may be 1'bx, + // and if so, discount them + if (GetSize(taps) < shiftx->getParam("\\A_WIDTH").as_int()) { + const SigSpec A = shiftx->getPort("\\A"); + const int A_width = shiftx->getParam("\\A_WIDTH").as_int(); + for (int i = GetSize(taps); i < A_width; ++i) + if (A[i] != RTLIL::Sx) return false; + } + else if (GetSize(taps) != shiftx->getParam("\\A_WIDTH").as_int()) return false; } else if (shiftx->type == "$mux") { @@ -596,6 +606,9 @@ struct ShregmapPass : public Pass { log(" -tech greenpak4\n"); log(" map to greenpak4 shift registers.\n"); log("\n"); + log(" -tech xilinx\n"); + log(" map to xilinx dynamic-length shift registers.\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { diff --git a/passes/techmap/zinit.cc b/passes/techmap/zinit.cc index b46147fb9..2aefc091d 100644 --- a/passes/techmap/zinit.cc +++ b/passes/techmap/zinit.cc @@ -46,7 +46,7 @@ struct ZinitPass : public Pass { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-singleton") { + if (args[argidx] == "-all") { all_mode = true; continue; } diff --git a/techlibs/common/prep.cc b/techlibs/common/prep.cc index 86fb4d6c6..cdd21c3b3 100644 --- a/techlibs/common/prep.cc +++ b/techlibs/common/prep.cc @@ -195,9 +195,11 @@ struct PrepPass : public ScriptPass run(nokeepdc ? "opt" : "opt -keepdc"); if (!ifxmode) { if (help_mode) - run("wreduce [-memx]"); - else + run("wreduce -keepdc [-memx]"); + else if (nokeepdc) run(memxmode ? "wreduce -memx" : "wreduce"); + else + run(memxmode ? "wreduce -keepdc -memx" : "wreduce -keepdc"); } if (!nomemmode) { run(string("memory_dff") + (help_mode ? " [-nordff]" : nordff ? " -nordff" : "")); diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index 8e43fe058..a424d3089 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -1271,6 +1271,181 @@ endmodule // -------------------------------------------------------- +module \$specify2 (EN, SRC, DST); + +parameter FULL = 0; +parameter SRC_WIDTH = 1; +parameter DST_WIDTH = 1; + +parameter SRC_DST_PEN = 0; +parameter SRC_DST_POL = 0; + +parameter T_RISE_MIN = 0; +parameter T_RISE_TYP = 0; +parameter T_RISE_MAX = 0; + +parameter T_FALL_MIN = 0; +parameter T_FALL_TYP = 0; +parameter T_FALL_MAX = 0; + +input EN; +input [SRC_WIDTH-1:0] SRC; +input [DST_WIDTH-1:0] DST; + +localparam SD = SRC_DST_PEN ? (SRC_DST_POL ? 1 : 2) : 0; + +`ifdef SIMLIB_SPECIFY +specify + if (EN && SD==0 && !FULL) (SRC => DST) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && SD==0 && FULL) (SRC *> DST) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && SD==1 && !FULL) (SRC +=> DST) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && SD==1 && FULL) (SRC +*> DST) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && SD==2 && !FULL) (SRC -=> DST) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && SD==2 && FULL) (SRC -*> DST) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); +endspecify +`endif + +endmodule + +// -------------------------------------------------------- + +module \$specify3 (EN, SRC, DST, DAT); + +parameter FULL = 0; +parameter SRC_WIDTH = 1; +parameter DST_WIDTH = 1; + +parameter EDGE_EN = 0; +parameter EDGE_POL = 0; + +parameter SRC_DST_PEN = 0; +parameter SRC_DST_POL = 0; + +parameter DAT_DST_PEN = 0; +parameter DAT_DST_POL = 0; + +parameter T_RISE_MIN = 0; +parameter T_RISE_TYP = 0; +parameter T_RISE_MAX = 0; + +parameter T_FALL_MIN = 0; +parameter T_FALL_TYP = 0; +parameter T_FALL_MAX = 0; + +input EN; +input [SRC_WIDTH-1:0] SRC; +input [DST_WIDTH-1:0] DST, DAT; + +localparam ED = EDGE_EN ? (EDGE_POL ? 1 : 2) : 0; +localparam SD = SRC_DST_PEN ? (SRC_DST_POL ? 1 : 2) : 0; +localparam DD = DAT_DST_PEN ? (DAT_DST_POL ? 1 : 2) : 0; + +`ifdef SIMLIB_SPECIFY +specify + // DD=0 + + if (EN && DD==0 && SD==0 && ED==0 && !FULL) ( SRC => (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==0 && SD==0 && ED==0 && FULL) ( SRC *> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==0 && SD==0 && ED==1 && !FULL) (posedge SRC => (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==0 && SD==0 && ED==1 && FULL) (posedge SRC *> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==0 && SD==0 && ED==2 && !FULL) (negedge SRC => (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==0 && SD==0 && ED==2 && FULL) (negedge SRC *> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + + if (EN && DD==0 && SD==1 && ED==0 && !FULL) ( SRC +=> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==0 && SD==1 && ED==0 && FULL) ( SRC +*> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==0 && SD==1 && ED==1 && !FULL) (posedge SRC +=> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==0 && SD==1 && ED==1 && FULL) (posedge SRC +*> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==0 && SD==1 && ED==2 && !FULL) (negedge SRC +=> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==0 && SD==1 && ED==2 && FULL) (negedge SRC +*> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + + if (EN && DD==0 && SD==2 && ED==0 && !FULL) ( SRC -=> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==0 && SD==2 && ED==0 && FULL) ( SRC -*> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==0 && SD==2 && ED==1 && !FULL) (posedge SRC -=> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==0 && SD==2 && ED==1 && FULL) (posedge SRC -*> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==0 && SD==2 && ED==2 && !FULL) (negedge SRC -=> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==0 && SD==2 && ED==2 && FULL) (negedge SRC -*> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + + // DD=1 + + if (EN && DD==1 && SD==0 && ED==0 && !FULL) ( SRC => (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==1 && SD==0 && ED==0 && FULL) ( SRC *> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==1 && SD==0 && ED==1 && !FULL) (posedge SRC => (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==1 && SD==0 && ED==1 && FULL) (posedge SRC *> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==1 && SD==0 && ED==2 && !FULL) (negedge SRC => (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==1 && SD==0 && ED==2 && FULL) (negedge SRC *> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + + if (EN && DD==1 && SD==1 && ED==0 && !FULL) ( SRC +=> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==1 && SD==1 && ED==0 && FULL) ( SRC +*> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==1 && SD==1 && ED==1 && !FULL) (posedge SRC +=> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==1 && SD==1 && ED==1 && FULL) (posedge SRC +*> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==1 && SD==1 && ED==2 && !FULL) (negedge SRC +=> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==1 && SD==1 && ED==2 && FULL) (negedge SRC +*> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + + if (EN && DD==1 && SD==2 && ED==0 && !FULL) ( SRC -=> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==1 && SD==2 && ED==0 && FULL) ( SRC -*> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==1 && SD==2 && ED==1 && !FULL) (posedge SRC -=> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==1 && SD==2 && ED==1 && FULL) (posedge SRC -*> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==1 && SD==2 && ED==2 && !FULL) (negedge SRC -=> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==1 && SD==2 && ED==2 && FULL) (negedge SRC -*> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + + // DD=2 + + if (EN && DD==2 && SD==0 && ED==0 && !FULL) ( SRC => (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==2 && SD==0 && ED==0 && FULL) ( SRC *> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==2 && SD==0 && ED==1 && !FULL) (posedge SRC => (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==2 && SD==0 && ED==1 && FULL) (posedge SRC *> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==2 && SD==0 && ED==2 && !FULL) (negedge SRC => (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==2 && SD==0 && ED==2 && FULL) (negedge SRC *> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + + if (EN && DD==2 && SD==1 && ED==0 && !FULL) ( SRC +=> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==2 && SD==1 && ED==0 && FULL) ( SRC +*> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==2 && SD==1 && ED==1 && !FULL) (posedge SRC +=> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==2 && SD==1 && ED==1 && FULL) (posedge SRC +*> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==2 && SD==1 && ED==2 && !FULL) (negedge SRC +=> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==2 && SD==1 && ED==2 && FULL) (negedge SRC +*> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + + if (EN && DD==2 && SD==2 && ED==0 && !FULL) ( SRC -=> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==2 && SD==2 && ED==0 && FULL) ( SRC -*> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==2 && SD==2 && ED==1 && !FULL) (posedge SRC -=> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==2 && SD==2 && ED==1 && FULL) (posedge SRC -*> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==2 && SD==2 && ED==2 && !FULL) (negedge SRC -=> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); + if (EN && DD==2 && SD==2 && ED==2 && FULL) (negedge SRC -*> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX); +endspecify +`endif + +endmodule + +// -------------------------------------------------------- + +module \$specrule (EN_SRC, EN_DST, SRC, DST); + +parameter TYPE = ""; +parameter T_LIMIT = 0; +parameter T_LIMIT2 = 0; + +parameter SRC_WIDTH = 1; +parameter DST_WIDTH = 1; + +parameter SRC_PEN = 0; +parameter SRC_POL = 0; + +parameter DST_PEN = 0; +parameter DST_POL = 0; + +input EN_SRC, EN_DST; +input [SRC_WIDTH-1:0] SRC; +input [DST_WIDTH-1:0] DST; + +`ifdef SIMLIB_SPECIFY +specify + // TBD +endspecify +`endif + +endmodule + +// -------------------------------------------------------- + module \$assert (A, EN); input A, EN; @@ -1863,4 +2038,5 @@ end endmodule `endif + // -------------------------------------------------------- diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index 349605f8c..ee2e86de9 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -209,6 +209,8 @@ struct SynthPass : public ScriptPass run("check"); run("opt"); run("wreduce"); + run("peepopt"); + run("opt_clean"); if (help_mode) run("techmap -map +/cmp2lut.v", " (if -lut)"); else diff --git a/techlibs/ecp5/cells_map.v b/techlibs/ecp5/cells_map.v index 6ab4b69f2..f6c71a03d 100644 --- a/techlibs/ecp5/cells_map.v +++ b/techlibs/ecp5/cells_map.v @@ -47,6 +47,21 @@ module \$__DFFSE_NP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED" module \$__DFFSE_PP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule module \$__DFFSE_PP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule +// Diamond I/O buffers +module IB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(I), .O(O)); endmodule +module IBPU (input I, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(I), .O(O)); endmodule +module IBPD (input I, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(I), .O(O)); endmodule +module OB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I)); endmodule +module OBZ (input I, T, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I), .T(T)); endmodule +module OBZPU(input I, T, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I), .T(T)); endmodule +module OBZPD(input I, T, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I), .T(T)); endmodule +module OBCO (input I, output OT, OC); OLVDS _TECHMAP_REPLACE_ (.A(I), .Z(OT), .ZN(OC)); endmodule +module BB (input I, T, output O, inout B); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.B(B), .I(I), .O(O), .T(T)); endmodule +module BBPU (input I, T, output O, inout B); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.B(B), .I(I), .O(O), .T(T)); endmodule +module BBPD (input I, T, output O, inout B); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.B(B), .I(I), .O(O), .T(T)); endmodule +module ILVDS(input A, AN, output Z); TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(A), .O(Z)); endmodule +module OLVDS(input A, output Z, ZN); TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(Z), .I(A)); endmodule + // For Diamond compatibility, FIXME: add all Diamond flipflop mappings module FD1S3BX(input PD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc index 4b889d672..c6e12248e 100644 --- a/techlibs/ecp5/synth_ecp5.cc +++ b/techlibs/ecp5/synth_ecp5.cc @@ -253,7 +253,7 @@ struct SynthEcp5Pass : public ScriptPass if (!nodffe) run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*"); run("techmap -D NO_LUT -map +/ecp5/cells_map.v"); - run("opt_expr -mux_undef"); + run("opt_expr -undriven -mux_undef"); run("simplemap"); run("ecp5_ffinit"); } diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v index 40972766d..560008bc4 100644 --- a/techlibs/ice40/cells_sim.v +++ b/techlibs/ice40/cells_sim.v @@ -957,10 +957,21 @@ endmodule (* blackbox *) module SB_HFOSC( + input TRIM0, + input TRIM1, + input TRIM2, + input TRIM3, + input TRIM4, + input TRIM5, + input TRIM6, + input TRIM7, + input TRIM8, + input TRIM9, input CLKHFPU, input CLKHFEN, output CLKHF ); +parameter TRIM_EN = "0b0"; parameter CLKHF_DIV = "0b00"; endmodule @@ -990,6 +1001,30 @@ parameter RGB2_CURRENT = "0b000000"; endmodule (* blackbox *) +module SB_LED_DRV_CUR( + input EN, + output LEDPU +); +endmodule + +(* blackbox *) +module SB_RGB_DRV( + input RGBLEDEN, + input RGB0PWM, + input RGB1PWM, + input RGB2PWM, + input RGBPU, + output RGB0, + output RGB1, + output RGB2 +); +parameter CURRENT_MODE = "0b0"; +parameter RGB0_CURRENT = "0b000000"; +parameter RGB1_CURRENT = "0b000000"; +parameter RGB2_CURRENT = "0b000000"; +endmodule + +(* blackbox *) module SB_I2C( input SBCLKI, input SBRWI, diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index 718f9d9e0..168161a90 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -245,11 +245,13 @@ struct SynthIce40Pass : public ScriptPass run("proc"); } - if (flatten && check_label("flatten", "(unless -noflatten)")) + if (check_label("flatten", "(unless -noflatten)")) { - run("flatten"); - run("tribuf -logic"); - run("deminout"); + if (flatten) { + run("flatten"); + run("tribuf -logic"); + run("deminout"); + } } if (check_label("coarse")) @@ -259,6 +261,8 @@ struct SynthIce40Pass : public ScriptPass run("check"); run("opt"); run("wreduce"); + run("peepopt"); + run("opt_clean"); run("share"); run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); run("opt_expr"); diff --git a/techlibs/intel/synth_intel.cc b/techlibs/intel/synth_intel.cc index d2291b8d4..639cba2c2 100644 --- a/techlibs/intel/synth_intel.cc +++ b/techlibs/intel/synth_intel.cc @@ -17,254 +17,243 @@ * */ -#include "kernel/register.h" #include "kernel/celltypes.h" -#include "kernel/rtlil.h" #include "kernel/log.h" +#include "kernel/register.h" +#include "kernel/rtlil.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct SynthIntelPass : public ScriptPass { - SynthIntelPass() : ScriptPass("synth_intel", "synthesis for Intel (Altera) FPGAs.") { } + SynthIntelPass() : ScriptPass("synth_intel", "synthesis for Intel (Altera) FPGAs.") {} - void help() YS_OVERRIDE - { - // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| - log("\n"); - log(" synth_intel [options]\n"); - log("\n"); - log("This command runs synthesis for Intel FPGAs.\n"); - log("\n"); - log(" -family < max10 | a10gx | cyclone10 | cyclonev | cycloneiv | cycloneive>\n"); - log(" generate the synthesis netlist for the specified family.\n"); - log(" MAX10 is the default target if no family argument specified.\n"); - log(" For Cyclone GX devices, use cycloneiv argument; For Cyclone E, use cycloneive.\n"); - log(" Cyclone V and Arria 10 GX devices are experimental, use it with a10gx argument.\n"); - log("\n"); - log(" -top <module>\n"); - log(" use the specified module as top module (default='top')\n"); - log("\n"); - log(" -vqm <file>\n"); - log(" write the design to the specified Verilog Quartus Mapping File. Writing of an\n"); - log(" output file is omitted if this parameter is not specified.\n"); - log("\n"); - log(" -vpr <file>\n"); - log(" write BLIF files for VPR flow experiments. The synthesized BLIF output file is not\n"); - log(" compatible with the Quartus flow. Writing of an\n"); - log(" output file is omitted if this parameter is not specified.\n"); - log("\n"); - log(" -run <from_label>:<to_label>\n"); - log(" only run the commands between the labels (see below). an empty\n"); - log(" from label is synonymous to 'begin', and empty to label is\n"); - log(" synonymous to the end of the command list.\n"); - log("\n"); - log(" -noiopads\n"); - log(" do not use altsyncram cells in output netlist\n"); - log("\n"); - log(" -nobram\n"); - log(" do not use altsyncram cells in output netlist\n"); - log("\n"); - log(" -noflatten\n"); - log(" do not flatten design before synthesis\n"); - log("\n"); - log(" -retime\n"); - log(" run 'abc' with -dff option\n"); - log("\n"); - log("The following commands are executed by this synthesis command:\n"); - help_script(); - log("\n"); - } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" synth_intel [options]\n"); + log("\n"); + log("This command runs synthesis for Intel FPGAs.\n"); + log("\n"); + log(" -family < max10 | a10gx | cyclone10 | cyclonev | cycloneiv | cycloneive>\n"); + log(" generate the synthesis netlist for the specified family.\n"); + log(" MAX10 is the default target if not family argument specified.\n"); + log(" For Cyclone GX devices, use cycloneiv argument; For Cyclone E, use cycloneive.\n"); + log(" Cyclone V and Arria 10 GX devices are experimental, use it with a10gx argument.\n"); + log("\n"); + log(" -top <module>\n"); + log(" use the specified module as top module (default='top')\n"); + log("\n"); + log(" -vqm <file>\n"); + log(" write the design to the specified Verilog Quartus Mapping File. Writing of an\n"); + log(" output file is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -vpr <file>\n"); + log(" write BLIF files for VPR flow experiments. The synthesized BLIF output file is not\n"); + log(" compatible with the Quartus flow. Writing of an\n"); + log(" output file is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -run <from_label>:<to_label>\n"); + log(" only run the commands between the labels (see below). an empty\n"); + log(" from label is synonymous to 'begin', and empty to label is\n"); + log(" synonymous to the end of the command list.\n"); + log("\n"); + log(" -noiopads\n"); + log(" do not use altsyncram cells in output netlist\n"); + log("\n"); + log(" -nobram\n"); + log(" do not use altsyncram cells in output netlist\n"); + log("\n"); + log(" -noflatten\n"); + log(" do not flatten design before synthesis\n"); + log("\n"); + log(" -retime\n"); + log(" run 'abc' with -dff option\n"); + log("\n"); + log("The following commands are executed by this synthesis command:\n"); + help_script(); + log("\n"); + } - string top_opt, family_opt, vout_file, blif_file; - bool retime, flatten, nobram, noiopads; + string top_opt, family_opt, vout_file, blif_file; + bool retime, flatten, nobram, noiopads; - void clear_flags() YS_OVERRIDE - { - top_opt = "-auto-top"; - family_opt = "max10"; - vout_file = ""; - blif_file = ""; - retime = false; - flatten = true; - nobram = false; - noiopads = false; - } + void clear_flags() YS_OVERRIDE + { + top_opt = "-auto-top"; + family_opt = "max10"; + vout_file = ""; + blif_file = ""; + retime = false; + flatten = true; + nobram = false; + noiopads = false; + } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE - { - string run_from, run_to; - clear_flags(); + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + string run_from, run_to; + clear_flags(); - size_t argidx; - for (argidx = 1; argidx < args.size(); argidx++) - { - if (args[argidx] == "-family" && argidx+1 < args.size()) { - family_opt = args[++argidx]; - continue; - } - if (args[argidx] == "-top" && argidx+1 < args.size()) { - top_opt = "-top " + args[++argidx]; - continue; - } - if (args[argidx] == "-vqm" && argidx+1 < args.size()) { - vout_file = args[++argidx]; - continue; - } - if (args[argidx] == "-vpr" && argidx+1 < args.size()) { - blif_file = args[++argidx]; - continue; - } - if (args[argidx] == "-run" && argidx+1 < args.size()) { - size_t pos = args[argidx+1].find(':'); - if (pos == std::string::npos) - break; - run_from = args[++argidx].substr(0, pos); - run_to = args[argidx].substr(pos+1); - continue; - } - if (args[argidx] == "-noiopads") { - noiopads = true; - continue; - } - if (args[argidx] == "-nobram") { - nobram = true; - continue; - } - if (args[argidx] == "-noflatten") { - flatten = false; - continue; - } - if (args[argidx] == "-retime") { - retime = true; - continue; - } - break; - } - extra_args(args, argidx, design); + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-family" && argidx + 1 < args.size()) { + family_opt = args[++argidx]; + continue; + } + if (args[argidx] == "-top" && argidx + 1 < args.size()) { + top_opt = "-top " + args[++argidx]; + continue; + } + if (args[argidx] == "-vqm" && argidx + 1 < args.size()) { + vout_file = args[++argidx]; + continue; + } + if (args[argidx] == "-vpr" && argidx + 1 < args.size()) { + blif_file = args[++argidx]; + continue; + } + if (args[argidx] == "-run" && argidx + 1 < args.size()) { + size_t pos = args[argidx + 1].find(':'); + if (pos == std::string::npos) + break; + run_from = args[++argidx].substr(0, pos); + run_to = args[argidx].substr(pos + 1); + continue; + } + if (args[argidx] == "-noiopads") { + noiopads = true; + continue; + } + if (args[argidx] == "-nobram") { + nobram = true; + continue; + } + if (args[argidx] == "-noflatten") { + flatten = false; + continue; + } + if (args[argidx] == "-retime") { + retime = true; + continue; + } + break; + } + extra_args(args, argidx, design); - if (!design->full_selection()) - log_cmd_error("This command only operates on fully selected designs!\n"); - if (family_opt != "max10" && family_opt !="a10gx" && family_opt != "cyclonev" && family_opt !="cycloneiv" && family_opt !="cycloneive" && family_opt != "cyclone10") - log_cmd_error("Invalid or no family specified: '%s'\n", family_opt.c_str()); + if (!design->full_selection()) + log_cmd_error("This command only operates on fully selected designs!\n"); + if (family_opt != "max10" && family_opt != "a10gx" && family_opt != "cyclonev" && family_opt != "cycloneiv" && + family_opt != "cycloneive" && family_opt != "cyclone10") + log_cmd_error("Invalid or not family specified: '%s'\n", family_opt.c_str()); - log_header(design, "Executing SYNTH_INTEL pass.\n"); - log_push(); + log_header(design, "Executing SYNTH_INTEL pass.\n"); + log_push(); - run_script(design, run_from, run_to); + run_script(design, run_from, run_to); - log_pop(); - } + log_pop(); + } - void script() YS_OVERRIDE - { - if (check_label("begin")) - { - if(check_label("family") && family_opt=="max10") - run("read_verilog -sv -lib +/intel/max10/cells_sim.v"); - else if(check_label("family") && family_opt=="a10gx") - run("read_verilog -sv -lib +/intel/a10gx/cells_sim.v"); - else if(check_label("family") && family_opt=="cyclonev") - run("read_verilog -sv -lib +/intel/cyclonev/cells_sim.v"); - else if(check_label("family") && family_opt=="cyclone10") - run("read_verilog -sv -lib +/intel/cyclone10/cells_sim.v"); - else if(check_label("family") && family_opt=="cycloneiv") - run("read_verilog -sv -lib +/intel/cycloneiv/cells_sim.v"); - else - run("read_verilog -sv -lib +/intel/cycloneive/cells_sim.v"); - // Misc and common cells - run("read_verilog -sv -lib +/intel/common/m9k_bb.v"); - run("read_verilog -sv -lib +/intel/common/altpll_bb.v"); - run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); - } + void script() YS_OVERRIDE + { + if (check_label("begin")) { + if (check_label("family") && family_opt == "max10") + run("read_verilog -sv -lib +/intel/max10/cells_sim.v"); + else if (check_label("family") && family_opt == "a10gx") + run("read_verilog -sv -lib +/intel/a10gx/cells_sim.v"); + else if (check_label("family") && family_opt == "cyclonev") + run("read_verilog -sv -lib +/intel/cyclonev/cells_sim.v"); + else if (check_label("family") && family_opt == "cyclone10") + run("read_verilog -sv -lib +/intel/cyclone10/cells_sim.v"); + else if (check_label("family") && family_opt == "cycloneiv") + run("read_verilog -sv -lib +/intel/cycloneiv/cells_sim.v"); + else + run("read_verilog -sv -lib +/intel/cycloneive/cells_sim.v"); + // Misc and common cells + run("read_verilog -sv -lib +/intel/common/m9k_bb.v"); + run("read_verilog -sv -lib +/intel/common/altpll_bb.v"); + run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); + } - if (flatten && check_label("flatten", "(unless -noflatten)")) - { - run("proc"); - run("flatten"); - run("tribuf -logic"); - run("deminout"); - } + if (flatten && check_label("flatten", "(unless -noflatten)")) { + run("proc"); + run("flatten"); + run("tribuf -logic"); + run("deminout"); + } - if (check_label("coarse")) - { - run("synth -run coarse"); - } + if (check_label("coarse")) { + run("synth -run coarse"); + } - if (!nobram && check_label("bram", "(skip if -nobram)")) - { - run("memory_bram -rules +/intel/common/brams.txt"); - run("techmap -map +/intel/common/brams_map.v"); - } + if (!nobram && check_label("bram", "(skip if -nobram)")) { + run("memory_bram -rules +/intel/common/brams.txt"); + run("techmap -map +/intel/common/brams_map.v"); + } - if (check_label("fine")) - { - run("opt -fast -mux_undef -undriven -fine -full"); - run("memory_map"); - run("opt -undriven -fine"); - run("dffsr2dff"); - run("dff2dffe -direct-match $_DFF_*"); - run("opt -fine"); - run("techmap -map +/techmap.v"); - run("opt -full"); - run("clean -purge"); - run("setundef -undriven -zero"); - if (retime || help_mode) - run("abc -markgroups -dff", "(only if -retime)"); - } + if (check_label("fine")) { + run("opt -fast -mux_undef -undriven -fine -full"); + run("memory_map"); + run("opt -undriven -fine"); + run("dffsr2dff"); + run("dff2dffe -direct-match $_DFF_*"); + run("opt -fine"); + run("techmap -map +/techmap.v"); + run("opt -full"); + run("clean -purge"); + run("setundef -undriven -zero"); + if (retime || help_mode) + run("abc -markgroups -dff", "(only if -retime)"); + } - if (check_label("map_luts")) - { - if(family_opt=="a10gx" || family_opt=="cyclonev") - run("abc -luts 2:2,3,6:5" + string(retime ? " -dff" : "")); - else - run("abc -lut 4" + string(retime ? " -dff" : "")); - run("clean"); - } + if (check_label("map_luts")) { + if (family_opt == "a10gx" || family_opt == "cyclonev") + run("abc -luts 2:2,3,6:5" + string(retime ? " -dff" : "")); + else + run("abc -lut 4" + string(retime ? " -dff" : "")); + run("clean"); + } - if (check_label("map_cells")) - { - if (!noiopads) - run("iopadmap -bits -outpad $__outpad I:O -inpad $__inpad O:I", "(unless -noiopads)"); - if(family_opt=="max10") - run("techmap -map +/intel/max10/cells_map.v"); - else if(family_opt=="a10gx") - run("techmap -map +/intel/a10gx/cells_map.v"); - else if(family_opt=="cyclonev") - run("techmap -map +/intel/cyclonev/cells_map.v"); - else if(family_opt=="cyclone10") - run("techmap -map +/intel/cyclone10/cells_map.v"); - else if(family_opt=="cycloneiv") - run("techmap -map +/intel/cycloneiv/cells_map.v"); - else - run("techmap -map +/intel/cycloneive/cells_map.v"); - run("dffinit -highlow -ff dffeas q power_up"); - run("clean -purge"); - } + if (check_label("map_cells")) { + if (!noiopads) + run("iopadmap -bits -outpad $__outpad I:O -inpad $__inpad O:I", "(unless -noiopads)"); + if (family_opt == "max10") + run("techmap -map +/intel/max10/cells_map.v"); + else if (family_opt == "a10gx") + run("techmap -map +/intel/a10gx/cells_map.v"); + else if (family_opt == "cyclonev") + run("techmap -map +/intel/cyclonev/cells_map.v"); + else if (family_opt == "cyclone10") + run("techmap -map +/intel/cyclone10/cells_map.v"); + else if (family_opt == "cycloneiv") + run("techmap -map +/intel/cycloneiv/cells_map.v"); + else + run("techmap -map +/intel/cycloneive/cells_map.v"); + run("dffinit -highlow -ff dffeas q power_up"); + run("clean -purge"); + } - if (check_label("check")) - { - run("hierarchy -check"); - run("stat"); - run("check -noinit"); - } + if (check_label("check")) { + run("hierarchy -check"); + run("stat"); + run("check -noinit"); + } - if (check_label("vqm")) - { - if (!vout_file.empty() || help_mode) - run(stringf("write_verilog -attr2comment -defparam -nohex -decimal -renameprefix syn_ %s", - help_mode ? "<file-name>" : vout_file.c_str())); - } + if (check_label("vqm")) { + if (!vout_file.empty() || help_mode) + run(stringf("write_verilog -attr2comment -defparam -nohex -decimal -renameprefix syn_ %s", + help_mode ? "<file-name>" : vout_file.c_str())); + } - if (check_label("vpr")) - { - if (!blif_file.empty() || help_mode) - { - run(stringf("opt_clean -purge")); - run(stringf("write_blif %s", help_mode ? "<file-name>" : blif_file.c_str())); - } - } - } + if (check_label("vpr")) { + if (!blif_file.empty() || help_mode) { + run(stringf("opt_clean -purge")); + run(stringf("write_blif %s", help_mode ? "<file-name>" : blif_file.c_str())); + } + } + } } SynthIntelPass; PRIVATE_NAMESPACE_END diff --git a/techlibs/xilinx/cells_map.v b/techlibs/xilinx/cells_map.v index 704ab21b1..40789ddbe 100644 --- a/techlibs/xilinx/cells_map.v +++ b/techlibs/xilinx/cells_map.v @@ -17,6 +17,16 @@ * */ +// Convert negative-polarity reset to positive-polarity +(* techmap_celltype = "$_DFF_NN0_" *) +module _90_dff_nn0_to_np0 (input D, C, R, output Q); \$_DFF_NP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule +(* techmap_celltype = "$_DFF_PN0_" *) +module _90_dff_pn0_to_pp0 (input D, C, R, output Q); \$_DFF_PP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule +(* techmap_celltype = "$_DFF_NN1_" *) +module _90_dff_nn1_to_np1 (input D, C, R, output Q); \$_DFF_NP1 _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule +(* techmap_celltype = "$_DFF_PN1_" *) +module _90_dff_pn1_to_pp1 (input D, C, R, output Q); \$_DFF_PP1 _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule + module \$__SHREG_ (input C, input D, input E, output Q); parameter DEPTH = 0; parameter [DEPTH-1:0] INIT = 0; diff --git a/techlibs/xilinx/drams.txt b/techlibs/xilinx/drams.txt index e6635d0e2..91632bcee 100644 --- a/techlibs/xilinx/drams.txt +++ b/techlibs/xilinx/drams.txt @@ -26,11 +26,15 @@ bram $__XILINX_RAM128X1D endbram match $__XILINX_RAM64X1D + min bits 5 + min wports 1 make_outreg or_next_if_better endmatch match $__XILINX_RAM128X1D + min bits 9 + min wports 1 make_outreg endmatch diff --git a/techlibs/xilinx/ff_map.v b/techlibs/xilinx/ff_map.v index c61fd7070..13beaa6ae 100644 --- a/techlibs/xilinx/ff_map.v +++ b/techlibs/xilinx/ff_map.v @@ -22,26 +22,21 @@ `ifndef _NO_FFS -`ifndef _NO_POS_SR module \$_DFF_N_ (input D, C, output Q); FDRE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule module \$_DFF_P_ (input D, C, output Q); FDRE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .R(1'b0)); endmodule module \$_DFFE_NP_ (input D, C, E, output Q); FDRE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0)); endmodule module \$_DFFE_PP_ (input D, C, E, output Q); FDRE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(E), .R(1'b0)); endmodule +module \$_DFF_NN0_ (input D, C, R, output Q); FDCE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R)); endmodule module \$_DFF_NP0_ (input D, C, R, output Q); FDCE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R)); endmodule +module \$_DFF_PN0_ (input D, C, R, output Q); FDCE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR(!R)); endmodule module \$_DFF_PP0_ (input D, C, R, output Q); FDCE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .CLR( R)); endmodule +module \$_DFF_NN1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R)); endmodule module \$_DFF_NP1_ (input D, C, R, output Q); FDPE_1 #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule +module \$_DFF_PN1_ (input D, C, R, output Q); FDPE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE(!R)); endmodule module \$_DFF_PP1_ (input D, C, R, output Q); FDPE #(.INIT(|0)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .CE(1'b1), .PRE( R)); endmodule -`endif - -module \$_DFF_NN0_ (input D, C, R, output Q); \$_DFF_NP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule -module \$_DFF_PN0_ (input D, C, R, output Q); \$_DFF_PP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule - -module \$_DFF_NN1_ (input D, C, R, output Q); \$_DFF_NP1 _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule -module \$_DFF_PN1_ (input D, C, R, output Q); \$_DFF_PP1 _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule -`endif `endif diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index 5820d6d61..f966115cd 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -25,18 +25,9 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool check_label(bool &active, std::string run_from, std::string run_to, std::string label) +struct SynthXilinxPass : public ScriptPass { - if (label == run_from) - active = true; - if (label == run_to) - active = false; - return active; -} - -struct SynthXilinxPass : public Pass -{ - SynthXilinxPass() : Pass("synth_xilinx", "synthesis for Xilinx FPGAs") { } + SynthXilinxPass() : ScriptPass("synth_xilinx", "synthesis for Xilinx FPGAs") { } void help() YS_OVERRIDE { @@ -51,6 +42,10 @@ struct SynthXilinxPass : public Pass log(" -top <module>\n"); log(" use the specified module as top module\n"); log("\n"); + log(" -arch {xcup|xcu|xc7|xc6s}\n"); + log(" run synthesis for the specified Xilinx architecture\n"); + log(" default: xc7\n"); + log("\n"); log(" -edif <file>\n"); log(" write the design to the specified edif file. writing of an output file\n"); log(" is omitted if this parameter is not specified.\n"); @@ -63,6 +58,9 @@ struct SynthXilinxPass : public Pass log(" generate an output netlist (and BLIF file) suitable for VPR\n"); log(" (this feature is experimental and incomplete)\n"); log("\n"); + log(" -nocarry\n"); + log(" disable inference of carry chains\n"); + log("\n"); log(" -nobram\n"); log(" disable inference of block rams\n"); log("\n"); @@ -72,6 +70,9 @@ struct SynthXilinxPass : public Pass log(" -nosrl\n"); log(" disable inference of shift registers\n"); log("\n"); + log(" -nomux\n"); + log(" disable inference of wide multiplexers\n"); + log("\n"); log(" -run <from_label>:<to_label>\n"); log(" only run the commands between the labels (see below). an empty\n"); log(" from label is synonymous to 'begin', and empty to label is\n"); @@ -88,80 +89,34 @@ struct SynthXilinxPass : public Pass log("\n"); log("\n"); log("The following commands are executed by this synthesis command:\n"); - log("\n"); - log(" begin:\n"); - log(" read_verilog -lib +/xilinx/cells_sim.v\n"); - log(" read_verilog -lib +/xilinx/cells_xtra.v\n"); - log(" read_verilog -lib +/xilinx/brams_bb.v\n"); - log(" hierarchy -check -top <top>\n"); - log("\n"); - log(" flatten: (only if -flatten)\n"); - log(" proc\n"); - log(" flatten\n"); - log("\n"); - log(" coarse:\n"); - log(" synth -run coarse\n"); - log("\n"); - log(" bram: (only executed when '-nobram' is not given)\n"); - log(" memory_bram -rules +/xilinx/brams.txt\n"); - log(" techmap -map +/xilinx/brams_map.v\n"); - log("\n"); - log(" dram: (only executed when '-nodram' is not given)\n"); - log(" memory_bram -rules +/xilinx/drams.txt\n"); - log(" techmap -map +/xilinx/drams_map.v\n"); - log("\n"); - log(" fine:\n"); - log(" opt -fast\n"); - log(" memory_map\n"); - log(" dffsr2dff\n"); - log(" dff2dffe\n"); - log(" techmap -map +/xilinx/arith_map.v\n"); - log(" opt -fast\n"); - log("\n"); - log(" map_cells:\n"); - log(" simplemap t:$dff t:$dffe (without '-nosrl' only)\n"); - log(" pmux2shiftx (without '-nosrl' only)\n"); - log(" opt_expr -mux_undef (without '-nosrl' only)\n"); - log(" shregmap -tech xilinx -minlen 3 (without '-nosrl' only)\n"); - log(" techmap -map +/xilinx/cells_map.v\n"); - log(" clean\n"); - log("\n"); - log(" map_luts:\n"); - log(" opt -full\n"); - log(" techmap -map +/techmap.v -D _NO_POS_SR -map +/xilinx/ff_map.v\n"); - log(" abc -luts 2:2,3,6:5,10,20 [-dff]\n"); - log(" clean\n"); - log(" shregmap -minlen 3 -init -params -enpol any_or_none (without '-nosrl' only)\n"); - log(" techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v"); - log(" dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT \\\n"); - log(" -ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT\n"); - log(" clean\n"); - log("\n"); - log(" check:\n"); - log(" hierarchy -check\n"); - log(" stat\n"); - log(" check -noinit\n"); - log("\n"); - log(" edif: (only if -edif)\n"); - log(" write_edif <file-name>\n"); - log("\n"); - log(" blif: (only if -blif)\n"); - log(" write_blif <file-name>\n"); + help_script(); log("\n"); } + + std::string top_opt, edif_file, blif_file, abc, arch; + bool flatten, retime, vpr, nocarry, nobram, nodram, nosrl, nomux; + + void clear_flags() YS_OVERRIDE + { + top_opt = "-auto-top"; + edif_file.clear(); + blif_file.clear(); + abc = "abc"; + flatten = false; + retime = false; + vpr = false; + nocarry = false; + nobram = false; + nodram = false; + nosrl = false; + nomux = false; + arch = "xc7"; + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { - std::string top_opt = "-auto-top"; - std::string edif_file; - std::string blif_file; std::string run_from, run_to; - std::string abc = "abc"; - bool flatten = false; - bool retime = false; - bool vpr = false; - bool nobram = false; - bool nodram = false; - bool nosrl = false; + clear_flags(); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -170,6 +125,10 @@ struct SynthXilinxPass : public Pass top_opt = "-top " + args[++argidx]; continue; } + if (args[argidx] == "-arch" && argidx+1 < args.size()) { + arch = args[++argidx]; + continue; + } if (args[argidx] == "-edif" && argidx+1 < args.size()) { edif_file = args[++argidx]; continue; @@ -198,6 +157,10 @@ struct SynthXilinxPass : public Pass vpr = true; continue; } + if (args[argidx] == "-nocarry") { + nocarry = true; + continue; + } if (args[argidx] == "-nobram") { nobram = true; continue; @@ -209,7 +172,11 @@ struct SynthXilinxPass : public Pass if (args[argidx] == "-nosrl") { nosrl = true; continue; - } + } + if (args[argidx] == "-nomux") { + nomux = true; + continue; + } if (args[argidx] == "-abc9") { abc = "abc9"; continue; @@ -218,131 +185,150 @@ struct SynthXilinxPass : public Pass } extra_args(args, argidx, design); + if (arch != "xcup" && arch != "xcu" && arch != "xc7" && arch != "xc6s") + log_cmd_error("Invalid Xilinx -arch setting: %s\n", arch.c_str()); + if (!design->full_selection()) log_cmd_error("This command only operates on fully selected designs!\n"); - bool active = run_from.empty(); - log_header(design, "Executing SYNTH_XILINX pass.\n"); log_push(); - if (check_label(active, run_from, run_to, "begin")) - { - if (vpr) { - Pass::call(design, "read_verilog -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v"); - } else { - Pass::call(design, "read_verilog -lib +/xilinx/cells_sim.v"); - } + run_script(design, run_from, run_to); - Pass::call(design, "read_verilog -lib +/xilinx/cells_xtra.v"); + log_pop(); + } - if (!nobram) { - Pass::call(design, "read_verilog -lib +/xilinx/brams_bb.v"); - } + void script() YS_OVERRIDE + { + if (check_label("begin")) { + if (vpr) + run("read_verilog -lib -D _ABC -D_EXPLICIT_CARRY +/xilinx/cells_sim.v"); + else + run("read_verilog -lib -D _ABC +/xilinx/cells_sim.v"); - Pass::call(design, stringf("hierarchy -check %s", top_opt.c_str())); - } + run("read_verilog -lib +/xilinx/cells_xtra.v"); - if (flatten && check_label(active, run_from, run_to, "flatten")) - { - Pass::call(design, "proc"); - Pass::call(design, "flatten"); - } + if (!nobram || help_mode) + run("read_verilog -lib +/xilinx/brams_bb.v", "(skip if '-nobram')"); - if (check_label(active, run_from, run_to, "coarse")) - { - Pass::call(design, "synth -run coarse"); + run(stringf("hierarchy -check %s", top_opt.c_str())); } - if (check_label(active, run_from, run_to, "bram")) - { - if (!nobram) { - Pass::call(design, "memory_bram -rules +/xilinx/brams.txt"); - Pass::call(design, "techmap -map +/xilinx/brams_map.v"); + if (check_label("flatten", "(with '-flatten' only)")) { + if (flatten || help_mode) { + run("proc"); + run("flatten"); } } - if (check_label(active, run_from, run_to, "dram")) - { - if (!nodram) { - Pass::call(design, "memory_bram -rules +/xilinx/drams.txt"); - Pass::call(design, "techmap -map +/xilinx/drams_map.v"); - } + if (check_label("coarse")) { + run("synth -run coarse"); + + //if (!nomux || help_mode) + // run("muxpack", "(skip if '-nomux')"); + + // shregmap -tech xilinx can cope with $shiftx and $mux + // cells for identifying variable-length shift registers, + // so attempt to convert $pmux-es to the former + // Also: wide multiplexer inference benefits from this too + if (!(nosrl && nomux) || help_mode) + run("pmux2shiftx", "(skip if '-nosrl' and '-nomux')"); + + // Run a number of peephole optimisations, including one + // that optimises $mul cells driving $shiftx's B input + // and that aids wide mux analysis + run("peepopt"); } - if (check_label(active, run_from, run_to, "fine")) - { - Pass::call(design, "opt -fast"); - Pass::call(design, "memory_map"); - Pass::call(design, "dffsr2dff"); - Pass::call(design, "dff2dffe"); - - if (vpr) { - Pass::call(design, "techmap -map +/xilinx/arith_map.v -D _EXPLICIT_CARRY"); - } else { - Pass::call(design, "techmap -map +/xilinx/arith_map.v"); + if (check_label("bram", "(skip if '-nobram')")) { + if (!nobram || help_mode) { + run("memory_bram -rules +/xilinx/brams.txt"); + run("techmap -map +/xilinx/brams_map.v"); } + } - Pass::call(design, "hierarchy -check"); - Pass::call(design, "opt -fast"); + if (check_label("dram", "(skip if '-nodram')")) { + if (!nodram || help_mode) { + run("memory_bram -rules +/xilinx/drams.txt"); + run("techmap -map +/xilinx/drams_map.v"); + } } - if (check_label(active, run_from, run_to, "map_cells")) - { - if (!nosrl) { + if (check_label("fine")) { + run("opt -fast -full"); + run("memory_map"); + run("dffsr2dff"); + run("dff2dffe"); + run("opt -full"); + + if (!nosrl || help_mode) { // shregmap operates on bit-level flops, not word-level, // so break those down here - Pass::call(design, "simplemap t:$dff t:$dffe"); - // shregmap -tech xilinx can cope with $shiftx and $mux - // cells for identifiying variable-length shift registers, - // so attempt to convert $pmux-es to the former - Pass::call(design, "pmux2shiftx"); - // pmux2shiftx can leave behind a $pmux with a single entry - // -- need this to clean that up before shregmap - Pass::call(design, "opt_expr -mux_undef"); + run("simplemap t:$dff t:$dffe", "(skip if '-nosrl')"); // shregmap with '-tech xilinx' infers variable length shift regs - Pass::call(design, "shregmap -tech xilinx -minlen 3"); + run("shregmap -tech xilinx -minlen 3", "(skip if '-nosrl')"); } - Pass::call(design, "techmap -map +/xilinx/cells_map.v"); - Pass::call(design, "clean"); + std::string techmap_files = " -map +/techmap.v"; + if (help_mode) + techmap_files += " [-map +/xilinx/mux_map.v]"; + else if (!nomux) + techmap_files += " -map +/xilinx/mux_map.v"; + if (help_mode) + techmap_files += " [-map +/xilinx/arith_map.v]"; + else if (!nocarry) { + techmap_files += " -map +/xilinx/arith_map.v"; + if (vpr) + techmap_files += " -D _EXPLICIT_CARRY"; + else if (abc == "abc9") + techmap_files += " -D _CLB_CARRY"; + } + run("techmap " + techmap_files); + run("opt -fast"); } - if (check_label(active, run_from, run_to, "map_luts")) - { - Pass::call(design, "opt -full"); - Pass::call(design, "techmap -map +/techmap.v -D _NO_POS_SR -map +/xilinx/ff_map.v"); - Pass::call(design, abc + " -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : "")); - Pass::call(design, "clean"); + if (check_label("map_cells")) { + if (!nomux || help_mode) + run("muxcover -mux8 -mux16", "(skip if '-nomux')"); + run("techmap -map +/techmap.v -map +/xilinx/cells_map.v"); + run("clean"); + } + + if (check_label("map_luts")) { + if (abc == "abc9") + run(abc + " -lut +/xilinx/abc.lut -box +/xilinx/abc.box -W 160" + string(retime ? " -dff" : "")); + else if (help_mode) + run(abc + " -luts 2:2,3,6:5,10,20 [-dff]"); + else + run(abc + " -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : "")); + run("clean"); + // This shregmap call infers fixed length shift registers after abc // has performed any necessary retiming - if (!nosrl) - Pass::call(design, "shregmap -minlen 3 -init -params -enpol any_or_none"); - Pass::call(design, "techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v"); - Pass::call(design, "dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT " + if (!nosrl || help_mode) + run("shregmap -minlen 3 -init -params -enpol any_or_none", "(skip if '-nosrl')"); + run("techmap -map +/xilinx/lut_map.v -map +/xilinx/cells_map.v -map +/xilinx/ff_map.v"); + run("dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT " "-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT"); - Pass::call(design, "clean"); + run("clean"); } - if (check_label(active, run_from, run_to, "check")) - { - Pass::call(design, "hierarchy -check"); - Pass::call(design, "stat"); - Pass::call(design, "check -noinit"); + if (check_label("check")) { + run("hierarchy -check"); + run("stat -tech xilinx"); + run("check -noinit"); } - if (check_label(active, run_from, run_to, "edif")) - { - if (!edif_file.empty()) - Pass::call(design, stringf("write_edif -pvector bra %s", edif_file.c_str())); - } - if (check_label(active, run_from, run_to, "blif")) - { - if (!blif_file.empty()) - Pass::call(design, stringf("write_blif %s", edif_file.c_str())); + if (check_label("edif")) { + if (!edif_file.empty() || help_mode) + run(stringf("write_edif -pvector bra %s", edif_file.c_str())); } - log_pop(); + if (check_label("blif")) { + if (!blif_file.empty() || help_mode) + run(stringf("write_blif %s", edif_file.c_str())); + } } } SynthXilinxPass; diff --git a/tests/aiger/and.aig b/tests/aiger/and.aig deleted file mode 100644 index da0fa0719..000000000 --- a/tests/aiger/and.aig +++ /dev/null @@ -1,3 +0,0 @@ -aig 3 2 0 1 1 -6 -
\ No newline at end of file diff --git a/tests/aiger/and.aag b/tests/aiger/and_.aag index d1ef2c5a5..cadd505f0 100644 --- a/tests/aiger/and.aag +++ b/tests/aiger/and_.aag @@ -3,3 +3,6 @@ aag 3 2 0 1 1 4 6 6 2 4 +i0 pi0 +i1 pi1 +o0 po0 diff --git a/tests/aiger/and_.aig b/tests/aiger/and_.aig new file mode 100644 index 000000000..13c7a0c17 --- /dev/null +++ b/tests/aiger/and_.aig @@ -0,0 +1,5 @@ +aig 3 2 0 1 1 +6 +i0 pi0 +i1 pi1 +o0 po0 diff --git a/tests/aiger/buffer.aag b/tests/aiger/buffer.aag index 94a6fb1ed..211106ed6 100644 --- a/tests/aiger/buffer.aag +++ b/tests/aiger/buffer.aag @@ -1,3 +1,5 @@ aag 1 1 0 1 0 2 2 +i0 pi0 +o0 po0 diff --git a/tests/aiger/buffer.aig b/tests/aiger/buffer.aig index 0c715fdeb..01df6f1cf 100644 --- a/tests/aiger/buffer.aig +++ b/tests/aiger/buffer.aig @@ -1,2 +1,4 @@ aig 1 1 0 1 0 2 +i0 pi0 +o0 po0 diff --git a/tests/aiger/cnt1.aag b/tests/aiger/cnt1.aag index ce4f28fcb..75598862c 100644 --- a/tests/aiger/cnt1.aag +++ b/tests/aiger/cnt1.aag @@ -1,3 +1,4 @@ aag 1 0 1 0 0 1 2 3 2 +b0 po0 diff --git a/tests/aiger/cnt1.aig b/tests/aiger/cnt1.aig index 8d0ba13b1..6fcf62522 100644 --- a/tests/aiger/cnt1.aig +++ b/tests/aiger/cnt1.aig @@ -1,3 +1,4 @@ aig 1 0 1 0 0 1 3 2 +b0 po0 diff --git a/tests/aiger/cnt1e.aag b/tests/aiger/cnt1e.aag index 6db3f0ffd..35cd5a482 100644 --- a/tests/aiger/cnt1e.aag +++ b/tests/aiger/cnt1e.aag @@ -6,3 +6,4 @@ aag 5 1 1 0 3 1 8 4 2 10 9 7 b0 AIGER_NEVER +i0 po0 diff --git a/tests/aiger/cnt1e.aig b/tests/aiger/cnt1e.aig index d8d159f11..7284dd42a 100644 --- a/tests/aiger/cnt1e.aig +++ b/tests/aiger/cnt1e.aig @@ -1,4 +1,5 @@ aig 5 1 1 0 3 1 10 4 -b0 AIGER_NEVER +i0 po0 +b0 AIGER_NEVER diff --git a/tests/aiger/false.aag b/tests/aiger/false.aag index 421e64a91..bab4a06a6 100644 --- a/tests/aiger/false.aag +++ b/tests/aiger/false.aag @@ -1,2 +1,3 @@ aag 0 0 0 1 0 0 +o0 po0 diff --git a/tests/aiger/false.aig b/tests/aiger/false.aig index ad7d039fa..4dc442d7b 100644 --- a/tests/aiger/false.aig +++ b/tests/aiger/false.aig @@ -1,2 +1,3 @@ aig 0 0 0 1 0 0 +o0 po0 diff --git a/tests/aiger/inverter.aag b/tests/aiger/inverter.aag index ff7c28542..428bad9e4 100644 --- a/tests/aiger/inverter.aag +++ b/tests/aiger/inverter.aag @@ -1,3 +1,5 @@ aag 1 1 0 1 0 2 3 +i0 pi0 +o0 po0 diff --git a/tests/aiger/inverter.aig b/tests/aiger/inverter.aig index 525d82392..5bec90ae3 100644 --- a/tests/aiger/inverter.aig +++ b/tests/aiger/inverter.aig @@ -1,2 +1,4 @@ aig 1 1 0 1 0 3 +i0 pi0 +o0 po0 diff --git a/tests/aiger/notcnt1e.aag b/tests/aiger/notcnt1e.aag index 141c864f7..2ed645d84 100644 --- a/tests/aiger/notcnt1e.aag +++ b/tests/aiger/notcnt1e.aag @@ -6,3 +6,4 @@ aag 5 1 1 0 3 1 8 4 2 10 9 7 b0 AIGER_NEVER +i0 pi0 diff --git a/tests/aiger/notcnt1e.aig b/tests/aiger/notcnt1e.aig index 7c85a7290..fd7e94508 100644 --- a/tests/aiger/notcnt1e.aig +++ b/tests/aiger/notcnt1e.aig @@ -1,4 +1,5 @@ aig 5 1 1 0 3 1 10 5 -b0 AIGER_NEVER +i0 pi0 +b0 AIGER_NEVER diff --git a/tests/aiger/or.aig b/tests/aiger/or.aig deleted file mode 100644 index 75c9e4480..000000000 --- a/tests/aiger/or.aig +++ /dev/null @@ -1,3 +0,0 @@ -aig 3 2 0 1 1 -7 -
\ No newline at end of file diff --git a/tests/aiger/or.aag b/tests/aiger/or_.aag index f780e339f..0f619dba3 100644 --- a/tests/aiger/or.aag +++ b/tests/aiger/or_.aag @@ -3,3 +3,6 @@ aag 3 2 0 1 1 4 7 6 3 5 +i0 pi0 +i1 pi1 +o0 po0 diff --git a/tests/aiger/or_.aig b/tests/aiger/or_.aig new file mode 100644 index 000000000..051687512 --- /dev/null +++ b/tests/aiger/or_.aig @@ -0,0 +1,5 @@ +aig 3 2 0 1 1 +7 +i0 pi0 +i1 pi1 +o0 po0 diff --git a/tests/aiger/run-test.sh b/tests/aiger/run-test.sh index e0a34f023..f52eb4ac1 100755 --- a/tests/aiger/run-test.sh +++ b/tests/aiger/run-test.sh @@ -1,24 +1,42 @@ #!/bin/bash -OPTIND=1 -seed="" # default to no seed specified -while getopts "S:" opt -do - case "$opt" in - S) arg="${OPTARG#"${OPTARG%%[![:space:]]*}"}" # remove leading space - seed="SEED=$arg" ;; - esac -done -shift "$((OPTIND-1))" +set -e -# check for Icarus Verilog -if ! which iverilog > /dev/null ; then - echo "$0: Error: Icarus Verilog 'iverilog' not found." - exit 1 -fi +# NB: *.aag and *.aig must contain a symbol table naming the primary +# inputs and outputs, otherwise ABC and Yosys will name them +# arbitrarily (and inconsistently with each other). -echo "===== AAG ======" -${MAKE:-make} -f ../tools/autotest.mk $seed *.aag EXTRA_FLAGS="-f aiger" +for aag in *.aag; do + # Since ABC cannot read *.aag, read the *.aig instead + # (which would have been created by the reference aig2aig utility, + # available from http://fmv.jku.at/aiger/) + ../../yosys-abc -c "read -c ${aag%.*}.aig; write ${aag%.*}_ref.v" + ../../yosys -p " +read_verilog ${aag%.*}_ref.v +prep +design -stash gold +read_aiger -clk_name clock $aag +prep +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports -seq 16 miter +" +done -echo "===== AIG ======" -exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.aig EXTRA_FLAGS="-f aiger" +for aig in *.aig; do + ../../yosys-abc -c "read -c $aig; write ${aig%.*}_ref.v" + ../../yosys -p " +read_verilog ${aig%.*}_ref.v +prep +design -stash gold +read_aiger -clk_name clock $aig +prep +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports -seq 16 miter +" +done diff --git a/tests/aiger/toggle.aag b/tests/aiger/toggle.aag index 09651012d..b1a1582d7 100644 --- a/tests/aiger/toggle.aag +++ b/tests/aiger/toggle.aag @@ -2,3 +2,5 @@ aag 1 0 1 2 0 2 3 2 3 +o0 po0 +o1 po1 diff --git a/tests/aiger/toggle.aig b/tests/aiger/toggle.aig index b69e21aaf..68b41763f 100644 --- a/tests/aiger/toggle.aig +++ b/tests/aiger/toggle.aig @@ -2,3 +2,5 @@ aig 1 0 1 2 0 3 2 3 +o0 po0 +o1 po1 diff --git a/tests/aiger/true.aag b/tests/aiger/true.aag index 366893648..66a9eab46 100644 --- a/tests/aiger/true.aag +++ b/tests/aiger/true.aag @@ -1,2 +1,3 @@ aag 0 0 0 1 0 1 +o0 po0 diff --git a/tests/aiger/true.aig b/tests/aiger/true.aig index 10086f389..f9bad6000 100644 --- a/tests/aiger/true.aig +++ b/tests/aiger/true.aig @@ -1,2 +1,3 @@ aig 0 0 0 1 0 1 +o0 po0 diff --git a/tests/memories/firrtl_938.v b/tests/memories/firrtl_938.v new file mode 100644 index 000000000..af5efcd25 --- /dev/null +++ b/tests/memories/firrtl_938.v @@ -0,0 +1,22 @@ +module top +( + input [7:0] data_a, + input [6:1] addr_a, + input we_a, clk, + output reg [7:0] q_a +); + // Declare the RAM variable + reg [7:0] ram[63:0]; + + // Port A + always @ (posedge clk) + begin + if (we_a) + begin + ram[addr_a] <= data_a; + q_a <= data_a; + end + q_a <= ram[addr_a]; + end + +endmodule diff --git a/tests/simple/attrib01_module.v b/tests/simple/attrib01_module.v new file mode 100644 index 000000000..adef34f5b --- /dev/null +++ b/tests/simple/attrib01_module.v @@ -0,0 +1,21 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output reg out; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= ~inp; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output wire out; + + bar bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/attrib02_port_decl.v b/tests/simple/attrib02_port_decl.v new file mode 100644 index 000000000..3505e7265 --- /dev/null +++ b/tests/simple/attrib02_port_decl.v @@ -0,0 +1,25 @@ +module bar(clk, rst, inp, out); + (* this_is_clock = 1 *) + input wire clk; + (* this_is_reset = 1 *) + input wire rst; + input wire inp; + (* an_output_register = 1*) + output reg out; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= ~inp; + +endmodule + +module foo(clk, rst, inp, out); + (* this_is_the_master_clock *) + input wire clk; + input wire rst; + input wire inp; + output wire out; + + bar bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/attrib03_parameter.v b/tests/simple/attrib03_parameter.v new file mode 100644 index 000000000..562d225cd --- /dev/null +++ b/tests/simple/attrib03_parameter.v @@ -0,0 +1,28 @@ +module bar(clk, rst, inp, out); + + (* bus_width *) + parameter WIDTH = 2; + + (* an_attribute_on_localparam = 55 *) + localparam INCREMENT = 5; + + input wire clk; + input wire rst; + input wire [WIDTH-1:0] inp; + output reg [WIDTH-1:0] out; + + always @(posedge clk) + if (rst) out <= 0; + else out <= inp + INCREMENT; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire [7:0] inp; + output wire [7:0] out; + + bar # (.WIDTH(8)) bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/attrib04_net_var.v b/tests/simple/attrib04_net_var.v new file mode 100644 index 000000000..8b5523406 --- /dev/null +++ b/tests/simple/attrib04_net_var.v @@ -0,0 +1,32 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output reg out; + + (* this_is_a_prescaler *) + reg [7:0] counter; + + (* temp_wire *) + wire out_val; + + always @(posedge clk) + counter <= counter + 1; + + assign out_val = inp ^ counter[4]; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= out_val; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output wire out; + + bar bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/attrib05_port_conn.v.DISABLED b/tests/simple/attrib05_port_conn.v.DISABLED new file mode 100644 index 000000000..e20e66319 --- /dev/null +++ b/tests/simple/attrib05_port_conn.v.DISABLED @@ -0,0 +1,21 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output reg out; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= ~inp; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output wire out; + + bar bar_instance ( (* clock_connected *) clk, rst, (* this_is_the_input *) inp, out); +endmodule + diff --git a/tests/simple/attrib06_operator_suffix.v b/tests/simple/attrib06_operator_suffix.v new file mode 100644 index 000000000..e21173c58 --- /dev/null +++ b/tests/simple/attrib06_operator_suffix.v @@ -0,0 +1,23 @@ +module bar(clk, rst, inp_a, inp_b, out); + input wire clk; + input wire rst; + input wire [7:0] inp_a; + input wire [7:0] inp_b; + output reg [7:0] out; + + always @(posedge clk) + if (rst) out <= 0; + else out <= inp_a + (* ripple_adder *) inp_b; + +endmodule + +module foo(clk, rst, inp_a, inp_b, out); + input wire clk; + input wire rst; + input wire [7:0] inp_a; + input wire [7:0] inp_b; + output wire [7:0] out; + + bar bar_instance (clk, rst, inp_a, inp_b, out); +endmodule + diff --git a/tests/simple/attrib07_func_call.v.DISABLED b/tests/simple/attrib07_func_call.v.DISABLED new file mode 100644 index 000000000..f55ef2316 --- /dev/null +++ b/tests/simple/attrib07_func_call.v.DISABLED @@ -0,0 +1,21 @@ +function [7:0] do_add; + input [7:0] inp_a; + input [7:0] inp_b; + + do_add = inp_a + inp_b; + +endfunction + +module foo(clk, rst, inp_a, inp_b, out); + input wire clk; + input wire rst; + input wire [7:0] inp_a; + input wire [7:0] inp_b; + output wire [7:0] out; + + always @(posedge clk) + if (rst) out <= 0; + else out <= do_add (* combinational_adder *) (inp_a, inp_b); + +endmodule + diff --git a/tests/simple/attrib08_mod_inst.v b/tests/simple/attrib08_mod_inst.v new file mode 100644 index 000000000..c5a32234e --- /dev/null +++ b/tests/simple/attrib08_mod_inst.v @@ -0,0 +1,22 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output reg out; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= ~inp; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output wire out; + + (* my_module_instance = 99 *) + bar bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/attrib09_case.v b/tests/simple/attrib09_case.v new file mode 100644 index 000000000..8551bf9d0 --- /dev/null +++ b/tests/simple/attrib09_case.v @@ -0,0 +1,26 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire [1:0] inp; + output reg [1:0] out; + + always @(inp) + (* full_case, parallel_case *) + case(inp) + 2'd0: out <= 2'd3; + 2'd1: out <= 2'd2; + 2'd2: out <= 2'd1; + 2'd3: out <= 2'd0; + endcase + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire [1:0] inp; + output wire [1:0] out; + + bar bar_instance (clk, rst, inp, out); +endmodule + diff --git a/tests/simple/dff_init.v b/tests/simple/dff_init.v index be947042e..375ea5c4d 100644 --- a/tests/simple/dff_init.v +++ b/tests/simple/dff_init.v @@ -40,3 +40,15 @@ module dff1a_test(n1, n1_inv, clk); n1 <= n1_inv; assign n1_inv = ~n1; endmodule + +module dff_test_997 (y, clk, wire4); +// https://github.com/YosysHQ/yosys/issues/997 + output wire [1:0] y; + input clk; + input signed wire4; + reg [1:0] reg10 = 0; + always @(posedge clk) begin + reg10 <= wire4; + end + assign y = reg10; +endmodule diff --git a/tests/simple/forloops.v b/tests/simple/forloops.v new file mode 100644 index 000000000..8665222d8 --- /dev/null +++ b/tests/simple/forloops.v @@ -0,0 +1,25 @@ +module forloops01 (input clk, a, b, output reg [3:0] p, q, x, y); + integer k; + always @(posedge clk) begin + for (k=0; k<2; k=k+1) + p[2*k +: 2] = {a, b} ^ {2{k}}; + x <= k + {a, b}; + end + always @* begin + for (k=0; k<4; k=k+1) + q[k] = {~a, ~b, a, b} >> k[1:0]; + y = k - {a, b}; + end +endmodule + +module forloops02 (input clk, a, b, output reg [3:0] q, x, output [3:0] y); + integer k; + always @* begin + for (k=0; k<4; k=k+1) + q[k] = {~a, ~b, a, b} >> k[1:0]; + end + always @* begin + x = k + {a, b}; + end + assign y = k - {a, b}; +endmodule diff --git a/tests/simple/implicit_ports.v b/tests/simple/implicit_ports.v new file mode 100644 index 000000000..8b0a6f386 --- /dev/null +++ b/tests/simple/implicit_ports.v @@ -0,0 +1,16 @@ +// Test implicit port connections +module alu (input [2:0] a, input [2:0] b, input cin, output cout, output [2:0] result); + assign cout = cin; + assign result = a + b; +endmodule + +module named_ports(input [2:0] a, b, output [2:0] alu_result, output cout); + wire cin = 1; + alu alu ( + .a(a), + .b, // Implicit connection is equivalent to .b(b) + .cin(), // Explicitely unconnected + .cout(cout), + .result(alu_result) + ); +endmodule diff --git a/tests/simple/localparam_attr.v b/tests/simple/localparam_attr.v new file mode 100644 index 000000000..2ef76c71c --- /dev/null +++ b/tests/simple/localparam_attr.v @@ -0,0 +1,11 @@ +module uut_localparam_attr (I, O); + +(* LOCALPARAM_ATTRIBUTE = "attribute_content" *) +localparam WIDTH = 1; + +input wire [WIDTH-1:0] I; +output wire [WIDTH-1:0] O; + +assign O = I; + +endmodule diff --git a/tests/simple/mem2reg.v b/tests/simple/mem2reg.v index 9839fd4a8..100426785 100644 --- a/tests/simple/mem2reg.v +++ b/tests/simple/mem2reg.v @@ -92,3 +92,25 @@ module mem2reg_test5(input ctrl, output out); assign out = bar[foo[0]]; endmodule +// ------------------------------------------------------ + +module mem2reg_test6 (din, dout); + input wire [3:0] din; + output reg [3:0] dout; + + reg [1:0] din_array [1:0]; + reg [1:0] dout_array [1:0]; + + always @* begin + din_array[0] = din[0 +: 2]; + din_array[1] = din[2 +: 2]; + + dout_array[0] = din_array[0]; + dout_array[1] = din_array[1]; + + {dout_array[0][1], dout_array[0][0]} = dout_array[0][0] + dout_array[1][0]; + + dout[0 +: 2] = dout_array[0]; + dout[2 +: 2] = dout_array[1]; + end +endmodule diff --git a/tests/simple/param_attr.v b/tests/simple/param_attr.v new file mode 100644 index 000000000..34d63a34e --- /dev/null +++ b/tests/simple/param_attr.v @@ -0,0 +1,11 @@ +module uut_param_attr (I, O); + +(* PARAMETER_ATTRIBUTE = "attribute_content" *) +parameter WIDTH = 1; + +input wire [WIDTH-1:0] I; +output wire [WIDTH-1:0] O; + +assign O = I; + +endmodule diff --git a/tests/simple/peepopt.v b/tests/simple/peepopt.v new file mode 100644 index 000000000..1bf427897 --- /dev/null +++ b/tests/simple/peepopt.v @@ -0,0 +1,13 @@ +module peepopt_shiftmul_0 #(parameter N=3, parameter W=3) (input [N*W-1:0] i, input [$clog2(N)-1:0] s, output [W-1:0] o); +assign o = i[s*W+:W]; +endmodule + +module peepopt_shiftmul_1 (output y, input [2:0] w); +assign y = 1'b1 >> (w * (3'b110)); +endmodule + +module peepopt_muldiv_0(input [1:0] i, output [1:0] o); +wire [3:0] t; +assign t = i * 3; +assign o = t / 3; +endmodule diff --git a/tests/simple/run-test.sh b/tests/simple/run-test.sh index aaa1cf940..967ac49f2 100755 --- a/tests/simple/run-test.sh +++ b/tests/simple/run-test.sh @@ -17,4 +17,5 @@ if ! which iverilog > /dev/null ; then exit 1 fi -exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.v +shopt -s nullglob +exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.{sv,v} diff --git a/tests/simple/wandwor.v b/tests/simple/wandwor.v new file mode 100644 index 000000000..34404aa26 --- /dev/null +++ b/tests/simple/wandwor.v @@ -0,0 +1,36 @@ +module wandwor_test0 (A, B, C, D, X, Y, Z); + input A, B, C, D; + output wor X; + output wand Y; + output Z; + + assign X = A, X = B, Y = C, Y = D; + foo foo_0 (C, D, X); + foo foo_1 (A, B, Y); + foo foo_2 (X, Y, Z); +endmodule + +module wandwor_test1 (A, B, C, D, X, Y, Z); + input [3:0] A, B, C, D; + output wor [3:0] X; + output wand [3:0] Y; + output Z; + + bar bar_inst ( + .I0({A, B}), + .I1({B, A}), + .O({X, Y}) + ); + + assign X = C, X = D; + assign Y = C, Y = D; + assign Z = ^{X,Y}; +endmodule + +module foo(input I0, I1, output O); + assign O = I0 ^ I1; +endmodule + +module bar(input [7:0] I0, I1, output [7:0] O); + assign O = I0 + I1; +endmodule diff --git a/tests/simple/xfirrtl b/tests/simple/xfirrtl index 50d693513..ba61a4476 100644 --- a/tests/simple/xfirrtl +++ b/tests/simple/xfirrtl @@ -16,6 +16,7 @@ operators.v $pow partsel.v drops modules process.v drops modules realexpr.v drops modules +retime.v Initial value (11110101) for (retime_test.ff) not supported scopes.v original verilog issues ( -x where x isn't declared signed) sincos.v $adff specify.v no code (empty module generates error diff --git a/tests/svinterfaces/runone.sh b/tests/svinterfaces/runone.sh index 0adecc797..54cf5f2ec 100755 --- a/tests/svinterfaces/runone.sh +++ b/tests/svinterfaces/runone.sh @@ -11,12 +11,12 @@ echo "" > $STDERRFILE echo -n "Test: ${TESTNAME} -> " -$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}.sv ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_syn.v" >> $STDOUTFILE >> $STDERRFILE -$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}_ref.v ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_ref_syn.v" >> $STDOUTFILE >> $STDERRFILE +set -e -rm -f a.out reference_result.txt dut_result.txt +$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}.sv ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_syn.v" >> $STDOUTFILE 2>> $STDERRFILE +$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}_ref.v ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_ref_syn.v" >> $STDOUTFILE 2>> $STDERRFILE -set -e +rm -f a.out reference_result.txt dut_result.txt iverilog -g2012 ${TESTNAME}_syn.v iverilog -g2012 ${TESTNAME}_ref_syn.v diff --git a/tests/tools/autotest.sh b/tests/tools/autotest.sh index 86a90793e..c394b9817 100755 --- a/tests/tools/autotest.sh +++ b/tests/tools/autotest.sh @@ -89,6 +89,13 @@ done compile_and_run() { exe="$1"; output="$2"; shift 2 + ext=${1##*.} + if [ "$ext" == "sv" ]; then + language_gen="-g2012" + else + language_gen="-g2005" + fi + if $use_modelsim; then altver=$( ls -v /opt/altera/ | grep '^[0-9]' | tail -n1; ) /opt/altera/$altver/modelsim_ase/bin/vlib work @@ -99,7 +106,7 @@ compile_and_run() { /opt/Xilinx/Vivado/$xilver/bin/xvlog $xinclude_opts -d outfile=\"$output\" "$@" /opt/Xilinx/Vivado/$xilver/bin/xelab -R work.testbench else - iverilog $include_opts -Doutfile=\"$output\" -s testbench -o "$exe" "$@" + iverilog $language_gen $include_opts -Doutfile=\"$output\" -s testbench -o "$exe" "$@" vvp -n "$exe" fi } @@ -110,7 +117,7 @@ for fn do bn=${fn%.*} ext=${fn##*.} - if [[ "$ext" != "v" ]] && [[ "$ext" != "aag" ]] && [[ "$ext" != "aig" ]]; then + if [[ "$ext" != "v" ]] && [[ "$ext" != "sv" ]] && [[ "$ext" != "aag" ]] && [[ "$ext" != "aig" ]]; then echo "Invalid argument: $fn" >&2 exit 1 fi @@ -123,6 +130,10 @@ do echo -n "Test: $bn " fi + if [ "$ext" == sv ]; then + frontend="$frontend -sv" + fi + rm -f ${bn}.{err,log,skip} mkdir -p ${bn}.out rm -rf ${bn}.out/* @@ -134,9 +145,10 @@ do if [[ "$ext" == "v" ]]; then egrep -v '^\s*`timescale' ../$fn > ${bn}_ref.${ext} + elif [[ "$ext" == "aig" ]] || [[ "$ext" == "aag" ]]; then + "$toolsdir"/../../yosys-abc -c "read_aiger ../${fn}; write ${bn}_ref.v" else - "$toolsdir"/../../yosys -f "$frontend $include_opts" -b "verilog" -o ${bn}_ref.v ../${fn} - frontend="verilog -noblackbox" + cp ../${fn} ${bn}_ref.${ext} fi rm -f ${bn}_ref.fir @@ -147,7 +159,8 @@ do 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.v $libs \ - "$toolsdir"/../../techlibs/common/simlib.v + "$toolsdir"/../../techlibs/common/simlib.v \ + "$toolsdir"/../../techlibs/common/simcells.v if $genvcd; then mv testbench.vcd ${bn}_ref.vcd; fi test_count=0 diff --git a/tests/various/attrib05_port_conn.v b/tests/various/attrib05_port_conn.v new file mode 100644 index 000000000..e20e66319 --- /dev/null +++ b/tests/various/attrib05_port_conn.v @@ -0,0 +1,21 @@ +module bar(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output reg out; + + always @(posedge clk) + if (rst) out <= 1'd0; + else out <= ~inp; + +endmodule + +module foo(clk, rst, inp, out); + input wire clk; + input wire rst; + input wire inp; + output wire out; + + bar bar_instance ( (* clock_connected *) clk, rst, (* this_is_the_input *) inp, out); +endmodule + diff --git a/tests/various/attrib05_port_conn.ys b/tests/various/attrib05_port_conn.ys new file mode 100644 index 000000000..27a016733 --- /dev/null +++ b/tests/various/attrib05_port_conn.ys @@ -0,0 +1,2 @@ +# Read and parse Verilog file +read_verilog attrib05_port_conn.v diff --git a/tests/various/attrib07_func_call.v b/tests/various/attrib07_func_call.v new file mode 100644 index 000000000..f55ef2316 --- /dev/null +++ b/tests/various/attrib07_func_call.v @@ -0,0 +1,21 @@ +function [7:0] do_add; + input [7:0] inp_a; + input [7:0] inp_b; + + do_add = inp_a + inp_b; + +endfunction + +module foo(clk, rst, inp_a, inp_b, out); + input wire clk; + input wire rst; + input wire [7:0] inp_a; + input wire [7:0] inp_b; + output wire [7:0] out; + + always @(posedge clk) + if (rst) out <= 0; + else out <= do_add (* combinational_adder *) (inp_a, inp_b); + +endmodule + diff --git a/tests/various/attrib07_func_call.ys b/tests/various/attrib07_func_call.ys new file mode 100644 index 000000000..774827651 --- /dev/null +++ b/tests/various/attrib07_func_call.ys @@ -0,0 +1,2 @@ +# Read and parse Verilog file +read_verilog attrib07_func_call.v diff --git a/tests/various/chparam.sh b/tests/various/chparam.sh new file mode 100644 index 000000000..9bb8d81db --- /dev/null +++ b/tests/various/chparam.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +trap 'echo "ERROR in chparam.sh" >&2; exit 1' ERR + +cat > chparam1.sv << "EOT" +module top #( + parameter [31:0] X = 0 +) ( + input [31:0] din, + output [31:0] dout +); + assign dout = X-din; +endmodule + +module top_props #( + parameter [31:0] X = 0 +) ( + input [31:0] dout +); + always @* assert (dout != X); +endmodule + +bind top top_props #(.X(123456789)) props (.*); +EOT + +cat > chparam2.sv << "EOT" +module top #( + parameter [31:0] X = 0 +) ( + input [31:0] din, + output [31:0] dout +); + assign dout = X-din; + always @* assert (dout != 123456789); +endmodule +EOT + +if ../../yosys -q -p 'verific -sv chparam1.sv'; then + ../../yosys -q -p 'verific -sv chparam1.sv; hierarchy -chparam X 123123123 -top top; prep -flatten' \ + -p 'sat -verify -prove-asserts -show-ports -set din[0] 1' \ + -p 'sat -falsify -prove-asserts -show-ports -set din[0] 0' + + ../../yosys -q -p 'verific -sv chparam2.sv; hierarchy -chparam X 123123123 -top top; prep -flatten' \ + -p 'sat -verify -prove-asserts -show-ports -set din[0] 1' \ + -p 'sat -falsify -prove-asserts -show-ports -set din[0] 0' +fi +../../yosys -q -p 'read_verilog -sv chparam2.sv; hierarchy -chparam X 123123123 -top top; prep -flatten' \ + -p 'sat -verify -prove-asserts -show-ports -set din[0] 1' \ + -p 'sat -falsify -prove-asserts -show-ports -set din[0] 0' + +rm chparam1.sv +rm chparam2.sv diff --git a/tests/various/elab_sys_tasks.sv b/tests/various/elab_sys_tasks.sv new file mode 100644 index 000000000..774d85b32 --- /dev/null +++ b/tests/various/elab_sys_tasks.sv @@ -0,0 +1,30 @@ +module test; +localparam X=1; +genvar i; +generate +if (X == 1) + $info("X is 1"); +if (X == 1) + $warning("X is 1"); +else + $error("X is not 1"); +case (X) + 1: $info("X is 1 in a case statement"); +endcase +//case (X-1) +// 1: $warn("X is 2"); +// default: $warn("X might be anything in a case statement"); +//endcase +for (i = 0; i < 3; i = i + 1) +begin + case(i) + 0: $info; + 1: $warning; + default: $info("default case statemnent"); + endcase +end + +$info("This is a standalone $info(). Next $info has no parameters"); +$info; +endgenerate +endmodule diff --git a/tests/various/elab_sys_tasks.ys b/tests/various/elab_sys_tasks.ys new file mode 100644 index 000000000..45bee3a60 --- /dev/null +++ b/tests/various/elab_sys_tasks.ys @@ -0,0 +1 @@ +read_verilog -sv elab_sys_tasks.sv diff --git a/tests/various/opt_rmdff.v b/tests/various/opt_rmdff.v new file mode 100644 index 000000000..b1c06703c --- /dev/null +++ b/tests/various/opt_rmdff.v @@ -0,0 +1,50 @@ +module opt_rmdff_test (input C, input D, input E, output [29:0] Q); +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove0 (.CLK(C), .D(D), .EN(1'b0), .Q(Q[0])); // EN is never active +(* init = "1'b1" *) wire Q1; assign Q[1] = Q1; +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove1 (.CLK(C), .D(D), .EN(1'b0), .Q(Q1)); // EN is never active +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove2 (.CLK(C), .D(D), .EN(1'bx), .Q(Q[2])); // EN is don't care +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) keep3 (.CLK(C), .D(D), .EN(1'b1), .Q(Q[3])); // EN is always active +(* init = "1'b0" *) wire Q4; assign Q[4] = Q4; +\$dffe #(.WIDTH(1), .CLK_POLARITY(0), .EN_POLARITY(1)) keep4 (.CLK(C), .D(D), .EN(1'b1), .Q(Q4)); // EN is always active +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(0)) remove5 (.CLK(C), .D(D), .EN(1'b1), .Q(Q[5])); // EN is never active +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(0)) remove6 (.CLK(C), .D(D), .EN(1'bx), .Q(Q[6])); // EN is don't care +(* init = "1'b0" *) wire Q7; assign Q[7] = Q7; +\$dffe #(.WIDTH(1), .CLK_POLARITY(0), .EN_POLARITY(0)) keep7 (.CLK(C), .D(D), .EN(E), .Q(Q7)); // EN is non constant + +\$_DFFE_PP_ remove8 (.C(C), .D(D), .E(1'b0), .Q(Q[8])); // EN is never active +(* init = "1'b1" *) wire Q9; assign Q[9] = Q9; +\$_DFFE_PP_ remove9 (.C(C), .D(D), .E(1'b0), .Q(Q9)); // EN is never active +\$_DFFE_PP_ remove10 (.C(C), .D(D), .E(1'bx), .Q(Q[10])); // EN is don't care +\$_DFFE_PP_ keep11 (.C(C), .D(D), .E(1'b1), .Q(Q[11])); // EN is always active +(* init = "1'b0" *) wire Q12; assign Q[12] = Q12; +\$_DFFE_PP_ keep12 (.C(C), .D(D), .E(1'b1), .Q(Q12)); // EN is always active + +\$_DFFE_NN_ remove13 (.C(C), .D(D), .E(1'b1), .Q(Q[13])); // EN is never active +(* init = "1'b1" *) wire Q14; assign Q[14] = Q14; +\$_DFFE_NN_ remove14 (.C(C), .D(D), .E(1'b1), .Q(Q14)); // EN is never active +\$_DFFE_NN_ remove15 (.C(C), .D(D), .E(1'bx), .Q(Q[15])); // EN is don't care +\$_DFFE_NN_ keep16 (.C(C), .D(D), .E(1'b0), .Q(Q[16])); // EN is always active +(* init = "1'b0" *) wire Q17; assign Q[17] = Q17; +\$_DFFE_NN_ keep17 (.C(C), .D(D), .E(1'b0), .Q(Q17)); // EN is always active + +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove18 (.CLK(1'b0), .D(D), .EN(E), .Q(Q[18])); // CLK is constant +(* init = "1'b1" *) wire Q19; assign Q[19] = Q19; +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove19 (.CLK(1'b1), .D(D), .EN(E), .Q(Q19)); // CLK is constant +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove20 (.CLK(C), .D(1'bx), .EN(E), .Q(Q[20])); // D is undriven, Q has no initial value +(* init = "1'b0" *) wire Q21; assign Q[21] = Q21; +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) keep21 (.CLK(C), .D(1'bx), .EN(E), .Q(Q21)); // D is undriven, Q has initial value +//\$dffe #(.WIDTH(1), .CLK_POLARITY(0), .EN_POLARITY(1)) remove22 (.CLK(C), .D(1'b0), .EN(1'b1), .Q(Q[22])); // D is constant, no initial Q value, EN is always active +// // (TODO, Q starts with 1'bx and becomes 1'b0) +(* init = "1'b0" *) wire Q23; assign Q[23] = Q23; +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) noenable23 (.CLK(C), .D(1'b0), .EN(1'b1), .Q(Q23)); // D is constant, initial Q value same as D, EN is always active +(* init = "1'b1" *) wire Q24; assign Q[24] = Q24; +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(0)) keep24 (.CLK(C), .D(1'b0), .EN(1'b0), .Q(Q24)); // D is constant, initial Q value NOT same as D, EN is always active +(* init = "1'b1" *) wire Q25; assign Q[25] = Q25; +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(0)) remove25 (.CLK(C), .D(1'b0), .EN(1'b1), .Q(Q25)); // D is constant, EN is never active +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) remove26 (.CLK(C), .D(Q[26]), .EN(1'b1), .Q(Q[26])); // D is Q, EN is always active +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(0)) remove27 (.CLK(C), .D(Q[27]), .EN(1'b1), .Q(Q[27])); // D is Q, EN is never active, but no initial value +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(0)) remove28 (.CLK(C), .D(Q[28]), .EN(E), .Q(Q[28])); // EN is nonconst, but no initial value +(* init = "1'b1" *) wire Q29; assign Q[29] = Q29; +\$dffe #(.WIDTH(1), .CLK_POLARITY(1), .EN_POLARITY(1)) keep29 (.CLK(C), .D(Q[29]), .EN(1'b1), .Q(Q29)); // EN is always active, but with initial value + +endmodule diff --git a/tests/various/opt_rmdff.ys b/tests/various/opt_rmdff.ys new file mode 100644 index 000000000..081f81782 --- /dev/null +++ b/tests/various/opt_rmdff.ys @@ -0,0 +1,26 @@ +read_verilog -icells opt_rmdff.v +prep +design -stash gold +read_verilog -icells opt_rmdff.v +proc +opt_rmdff + +select -assert-count 0 c:remove* +select -assert-min 7 c:keep* +select -assert-count 0 t:$dffe 7:$_DFFE_* %u c:noenable* %i + +design -stash gate + +design -import gold -as gold +design -import gate -as gate + +equiv_make gold gate equiv +hierarchy -top equiv +equiv_simple -undef +equiv_status -assert + +design -load gold +stat + +design -load gate +stat diff --git a/tests/various/specify.v b/tests/various/specify.v new file mode 100644 index 000000000..afc421da8 --- /dev/null +++ b/tests/various/specify.v @@ -0,0 +1,30 @@ +module test ( + input EN, CLK, + input [3:0] D, + output reg [3:0] Q +); + always @(posedge CLK) + if (EN) Q <= D; + + specify + if (EN) (CLK *> (Q : D)) = (1, 2:3:4); + $setup(D, posedge CLK &&& EN, 5); + $hold(posedge CLK, D &&& EN, 6); + endspecify +endmodule + +module test2 ( + input A, B, + output Q +); + xor (Q, A, B); + specify + //specparam T_rise = 1; + //specparam T_fall = 2; + `define T_rise 1 + `define T_fall 2 + (A => Q) = (`T_rise,`T_fall); + //(B => Q) = (`T_rise+`T_fall)/2.0; + (B => Q) = 1.5; + endspecify +endmodule diff --git a/tests/various/specify.ys b/tests/various/specify.ys new file mode 100644 index 000000000..a5ca07219 --- /dev/null +++ b/tests/various/specify.ys @@ -0,0 +1,56 @@ +read_verilog -specify specify.v +prep +cd test +select t:$specify2 -assert-count 0 +select t:$specify3 -assert-count 1 +select t:$specrule -assert-count 2 +cd test2 +select t:$specify2 -assert-count 2 +select t:$specify3 -assert-count 0 +select t:$specrule -assert-count 0 +cd +write_verilog specify.out +design -stash gold + +read_verilog -specify specify.out +prep +cd test +select t:$specify2 -assert-count 0 +select t:$specify3 -assert-count 1 +select t:$specrule -assert-count 2 +cd test2 +select t:$specify2 -assert-count 2 +select t:$specify3 -assert-count 0 +select t:$specrule -assert-count 0 +cd +design -stash gate + +design -copy-from gold -as gold test +design -copy-from gate -as gate test +rename -hide +rename -enumerate -pattern A_% t:$specify3 +rename -enumerate -pattern B_% t:$specrule r:TYPE=$setup %i +rename -enumerate -pattern C_% t:$specrule r:TYPE=$hold %i +select n:A_* -assert-count 2 +select n:B_* -assert-count 2 +select n:C_* -assert-count 2 +equiv_make gold gate equiv +hierarchy -top equiv +equiv_struct +equiv_induct -seq 5 +equiv_status -assert +design -reset + +design -copy-from gold -as gold test2 +design -copy-from gate -as gate test2 +rename -hide +rename -enumerate -pattern A_% t:$specify2 r:T_RISE_TYP=1 %i +rename -enumerate -pattern B_% t:$specify2 n:A_* %d +select n:A_* -assert-count 2 +select n:B_* -assert-count 2 +equiv_make gold gate equiv +hierarchy -top equiv +equiv_struct +equiv_induct -seq 5 +equiv_status -assert +design -reset |