diff options
45 files changed, 1091 insertions, 373 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-* @@ -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,30 +296,51 @@ 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 "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).print_includes()") endif +endif ifeq ($(ENABLE_READLINE),1) CXXFLAGS += -DYOSYS_ENABLE_READLINE @@ -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 - +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 "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 misc/__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,7 +257,7 @@ 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`` and ``disable`` keywords and library map files @@ -413,12 +413,13 @@ 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 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 fe6bdb8f6..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()); } @@ -449,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()); } @@ -562,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. @@ -575,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"; } diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index 2e4774dfd..38348cd65 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -55,11 +55,15 @@ void AigerReader::parse_aiger() // 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"); - } + 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 diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 5623541b2..83993eea9 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -194,6 +194,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 +725,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 +739,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 +784,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; diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 281cbe086..46d482f1a 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -173,7 +173,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 +262,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 +270,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 379fed641..d2651c9aa 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -904,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); @@ -919,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; @@ -963,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)); diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y index 0df792997..44c99906a 100644 --- a/frontends/ilang/ilang_parser.y +++ b/frontends/ilang/ilang_parser.y @@ -47,6 +47,15 @@ USING_YOSYS_NAMESPACE %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; int integer; @@ -451,4 +460,3 @@ conn_stmt: delete $2; delete $3; }; - diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 21a1bbbbe..2bf99e58e 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -48,6 +48,14 @@ USING_YOSYS_NAMESPACE #include "VhdlUnits.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 #endif @@ -2016,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 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_lexer.l b/frontends/verilog/verilog_lexer.l index 142d05d45..9558bbfb9 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -218,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; } @@ -232,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; } diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index d23009e60..8244a8f44 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -112,6 +112,15 @@ struct specify_rise_fall { %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; @@ -130,7 +139,7 @@ struct specify_rise_fall { %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 TOK_VAR -%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG TOK_LOGIC +%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 @@ -476,6 +485,12 @@ 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; } | @@ -1193,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 ';' { @@ -1201,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 ';' { @@ -2418,4 +2433,3 @@ concat_list: $$ = $3; $$->children.push_back($1); }; - diff --git a/kernel/log.cc b/kernel/log.cc index 9a9104e26..fa74a6a3c 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; } } @@ -285,6 +289,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 +305,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/rtlil.cc b/kernel/rtlil.cc index 79ff4a6a6..790ba52a3 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1514,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; @@ -1529,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); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index a0270bd1c..8509670ff 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -601,6 +601,7 @@ struct RTLIL::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; @@ -1001,6 +1002,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; @@ -1306,6 +1308,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); @@ -1324,6 +1327,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; }; @@ -1337,6 +1341,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; }; @@ -1347,6 +1352,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; }; @@ -1359,6 +1365,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; }; @@ -1421,12 +1428,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); @@ -1439,6 +1464,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); @@ -1447,6 +1483,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); @@ -1457,6 +1501,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); @@ -1464,6 +1517,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/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/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc index 85e2b0d56..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; 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/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/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 72bc2e133..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"); @@ -943,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; + + 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); - 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 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 sig = conn.second; + 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); + } - 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); + for (auto cell : module->cells()) + { + Module *m = design->module(cell->type); + + 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 6ca6ac820..7011d4602 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -232,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; @@ -272,20 +272,17 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos } } - SigPool raw_used_signals_noaliases; - for (auto &it : module->connections_) - raw_used_signals_noaliases.add(it.second); - 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); - raw_used_signals_noaliases.add(it2.second); if (!ct_all.cell_output(cell->type, it2.first)) used_signals_nodrivers.add(it2.second); } @@ -294,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) @@ -321,8 +319,9 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos wire->attributes.erase("\\init"); if (GetSize(wire) == 0) { - // delete zero-width wires - goto delete_this_wire; + // 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 @@ -330,11 +329,11 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos if (!purge_mode && check_public_name(wire->name)) { // do not get rid of public names unless in purge mode } else - if (!raw_used_signals_noaliases.check_any(s1)) { + if (!raw_used_signals.check_any(s1)) { // delete wires that aren't used by anything directly goto delete_this_wire; } else - if (!used_signals_nodrivers.check_any(s2)) { + 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; } @@ -400,6 +399,8 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos if (verbose && del_temp_wires_count) log_debug(" removed %d unused temporary wires.\n", del_temp_wires_count); + + return !del_wires_queue.empty(); } bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose) @@ -497,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 { 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..2abffa2a9 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[6] == 'P', 1); + val_ep = RTLIL::Const(dff->type[7] == '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 58c6e4b4b..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]; @@ -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); diff --git a/passes/pmgen/peepopt_shiftmul.pmg b/passes/pmgen/peepopt_shiftmul.pmg index fe861b728..6adab4e5f 100644 --- a/passes/pmgen/peepopt_shiftmul.pmg +++ b/passes/pmgen/peepopt_shiftmul.pmg @@ -1,4 +1,7 @@ pattern shiftmul +// +// Optimize mul+shift pairs that result from expressions such as foo[s*W+:W] +// state <SigSpec> shamt @@ -49,12 +52,16 @@ code 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_log2 = ceil_log2(const_factor); - int new_const_factor = 1 << new_const_factor_log2; - + 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; @@ -73,7 +80,7 @@ code if (trunc > 0) new_a.remove(GetSize(new_a)-trunc, trunc); - SigSpec new_b = {port(mul, const_factor_port == \A ? \B : \A), SigSpec(State::S0, new_const_factor_log2)}; + SigSpec new_b = {mul_din, SigSpec(State::S0, factor_bits)}; if (param(shift, \B_SIGNED).as_bool()) new_b.append(State::S0); 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/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/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/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v index 00843b97c..e89405b22 100644 --- a/techlibs/ice40/cells_sim.v +++ b/techlibs/ice40/cells_sim.v @@ -930,10 +930,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 diff --git a/techlibs/intel/synth_intel.cc b/techlibs/intel/synth_intel.cc index 0f1d7a7b5..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 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"); - } + 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 not 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/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/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index b022972c9..a293081f1 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -229,11 +229,6 @@ struct SynthXilinxPass : public ScriptPass run("dff2dffe"); run("opt -full"); - if (!vpr || help_mode) - run("techmap -map +/xilinx/arith_map.v"); - else - run("techmap -map +/xilinx/arith_map.v -D _EXPLICIT_CARRY"); - if (!nosrl || help_mode) { // shregmap operates on bit-level flops, not word-level, // so break those down here @@ -242,7 +237,11 @@ struct SynthXilinxPass : public ScriptPass run("shregmap -tech xilinx -minlen 3", "(skip if '-nosrl')"); } - run("techmap"); + if (!vpr || help_mode) + run("techmap -map +/techmap.v -map +/xilinx/arith_map.v"); + else + run("techmap -map +/techmap.v +/xilinx/arith_map.v -D _EXPLICIT_CARRY"); + run("opt -fast"); } @@ -269,7 +268,7 @@ struct SynthXilinxPass : public ScriptPass if (check_label("check")) { run("hierarchy -check"); - run("stat"); + run("stat -tech xilinx"); run("check -noinit"); } 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/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 index b27b9fe57..1bf427897 100644 --- a/tests/simple/peepopt.v +++ b/tests/simple/peepopt.v @@ -2,6 +2,10 @@ module peepopt_shiftmul_0 #(parameter N=3, parameter W=3) (input [N*W-1:0] i, in 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; 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/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 |