diff options
author | Sebastian Kuzminsky <seb@highlab.com> | 2016-02-29 21:47:04 -0700 |
---|---|---|
committer | Sebastian Kuzminsky <seb@highlab.com> | 2016-02-29 21:47:04 -0700 |
commit | 93b1bcd5dec9be1250e89e25405092469a9c8d34 (patch) | |
tree | 44fe541737e4c56c1ccafd7bd909b32a96e8ab21 | |
parent | 0fa23a0fe30e7570f2c8897c137e300af2e017f5 (diff) | |
parent | 5869d26da02147741f783a2f379fd49194c7e1ad (diff) |
Merge tag 'upstream/0.6' into new
127 files changed, 5276 insertions, 727 deletions
@@ -18,6 +18,7 @@ /yosys-abc /yosys-abc.exe /yosys-config +/yosys-smtbmc /yosys-filterlib /yosys-filterlib.exe /kernel/version_*.cc @@ -3,6 +3,94 @@ List of major changes and improvements between releases ======================================================= +Yosys 0.5 .. Yosys 0.6 +---------------------- + + * Various + - Added Contributor Covenant Code of Conduct + - Various improvements in dict<> and pool<> + - Added hashlib::mfp and refactored SigMap + - Improved support for reals as module parameters + - Various improvements in SMT2 back-end + - Added "keep_hierarchy" attribute + - Verilog front-end: define `BLACKBOX in -lib mode + - Added API for converting internal cells to AIGs + - Added ENABLE_LIBYOSYS Makefile option + - Removed "techmap -share_map" (use "-map +/filename" instead) + - Switched all Python scripts to Python 3 + - Added support for $display()/$write() and $finish() to Verilog front-end + - Added "yosys-smtbmc" formal verification flow + - Added options for clang sanitizers to Makefile + + * New commands and options + - Added "scc -expect <N> -nofeedback" + - Added "proc_dlatch" + - Added "check" + - Added "select %xe %cie %coe %M %C %R" + - Added "sat -dump_json" (WaveJSON format) + - Added "sat -tempinduct-baseonly -tempinduct-inductonly" + - Added "sat -stepsize" and "sat -tempinduct-step" + - Added "sat -show-regs -show-public -show-all" + - Added "write_json" (Native Yosys JSON format) + - Added "write_blif -attr" + - Added "dffinit" + - Added "chparam" + - Added "muxcover" + - Added "pmuxtree" + - Added memory_bram "make_outreg" feature + - Added "splice -wires" + - Added "dff2dffe -direct-match" + - Added simplemap $lut support + - Added "read_blif" + - Added "opt_share -share_all" + - Added "aigmap" + - Added "write_smt2 -mem -regs -wires" + - Added "memory -nordff" + - Added "write_smv" + - Added "synth -nordff -noalumacc" + - Added "rename -top new_name" + - Added "opt_const -clkinv" + - Added "synth -nofsm" + - Added "miter -assert" + - Added "read_verilog -noautowire" + - Added "read_verilog -nodpi" + - Added "tribuf" + - Added "lut2mux" + - Added "nlutmap" + - Added "qwp" + - Added "test_cell -noeval" + - Added "edgetypes" + - Added "equiv_struct" + - Added "equiv_purge" + - Added "equiv_mark" + - Added "equiv_add -try -cell" + - Added "singleton" + - Added "abc -g -luts" + - Added "torder" + - Added "write_blif -cname" + - Added "submod -copy" + - Added "dffsr2dff" + - Added "stat -liberty" + + * Synthesis metacommands + - Various improvements in synth_xilinx + - Added synth_ice40 and synth_greenpak4 + - Added "prep" metacommand for "synthesis lite" + + * Cell library changes + - Added cell types to "help" system + - Added $meminit cell type + - Added $assume cell type + - Added $_MUX4_, $_MUX8_, and $_MUX16_ cells + - Added $tribuf and $_TBUF_ cell types + - Added read-enable to memory model + + * YosysJS + - Various improvements in emscripten build + - Added alternative webworker-based JS API + - Added a few example applications + + Yosys 0.4 .. Yosys 0.5 ---------------------- diff --git a/CodeOfConduct b/CodeOfConduct new file mode 100644 index 00000000..4f779977 --- /dev/null +++ b/CodeOfConduct @@ -0,0 +1,73 @@ +Contributor Covenant Code of Conduct + +Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at clifford@clifford.at (and/or +cliffordvienna@gmail.com if you think your mail to the other address got +stuck in the spam filter). All complaints will be reviewed and investigated and +will result in a response that is deemed necessary and appropriate to the +circumstances. The project team is obligated to maintain confidentiality with +regard to the reporter of an incident. Further details of specific enforcement +policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +Attribution + +This Code of Conduct is adapted from the Contributor Covenant, version 1.4, +available at http://contributor-covenant.org/version/1/4/ diff --git a/CodingReadme b/CodingReadme index f93d2205..ba184be5 100644 --- a/CodingReadme +++ b/CodingReadme @@ -93,6 +93,9 @@ creates a bijective map from K to the integers. For example: It is not possible to remove elements from an idict. +Finally mfp<K> implements a merge-find set data structure (aka. disjoint-set or +union-find) over the type K ("mfp" = merge-find-promote). + 2. Standard STL data types In Yosys we use std::vector<T> and std::string whenever applicable. When @@ -342,10 +345,10 @@ Then with default config setting: ./yosys -p 'synth; show' tests/simple/fiedler-cooley.v ./yosys -p 'synth_xilinx -top up3down5; show' tests/simple/fiedler-cooley.v - cd ~yosys/techlibs/cmos + cd ~yosys/examples/cmos bash testbench.sh - cd ~yosys/techlibs/xilinx/example_basys3 + cd ~yosys/examples/basys3 bash run.sh @@ -18,6 +18,14 @@ ENABLE_LIBYOSYS := 0 ENABLE_GPROF := 0 ENABLE_NDEBUG := 0 +# clang sanitizers +SANITIZER = +# SANITIZER = address +# SANITIZER = memory +# SANITIZER = undefined +# SANITIZER = cfi + + PREFIX ?= /usr/local INSTALL_SUDO := @@ -54,13 +62,13 @@ ifeq (Darwin,$(findstring Darwin,$(shell uname))) LDFLAGS += $(shell PKG_CONFIG_PATH=$$(brew list libffi | grep pkgconfig | xargs dirname) pkg-config --silence-errors --libs libffi) # use bison installed by homebrew if available BISON = $(shell (brew list bison | grep -m1 "bin/bison") || echo bison) - SED = gsed + SED = sed else LDFLAGS += -rdynamic LDLIBS += -lrt endif -YOSYS_VER := 0.5+$(shell cd $(YOSYS_SRC) && test -d .git && { git log --author=clifford@clifford.at --oneline c3c9fbfb8c678.. | wc -l; }) +YOSYS_VER := 0.6 GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN) OBJS = kernel/version_$(GIT_REV).o @@ -70,7 +78,7 @@ OBJS = kernel/version_$(GIT_REV).o # is just a symlink to your actual ABC working directory, as 'make mrproper' # will remove the 'abc' directory and you do not want to accidentally # delete your work on ABC.. -ABCREV = c3698e053a7a +ABCREV = ae7d65e71adc ABCPULL = 1 ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" @@ -86,18 +94,39 @@ endif ifeq ($(CONFIG),clang) CXX = clang +LD = clang++ CXXFLAGS += -std=c++11 -Os +ifneq ($(SANITIZER),) +$(info [Clang Sanitizer] $(SANITIZER)) +CXXFLAGS += -g -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=$(SANITIZER) +LDFLAGS += -g -fsanitize=$(SANITIZER) +ifeq ($(SANITIZER),address) +ENABLE_COVER := 0 +endif +ifeq ($(SANITIZER),memory) +CXXFLAGS += -fPIE +LDFLAGS += -fPIE +endif +ifeq ($(SANITIZER),cfi) +CXXFLAGS += -flto +LDFLAGS += -flto +endif +endif + else ifeq ($(CONFIG),gcc) CXX = gcc +LD = gcc CXXFLAGS += -std=gnu++0x -Os else ifeq ($(CONFIG),gcc-4.6) CXX = gcc-4.6 +LD = gcc-4.6 CXXFLAGS += -std=gnu++0x -Os else ifeq ($(CONFIG),emcc) CXX = emcc +LD = emcc CXXFLAGS := -std=c++11 $(filter-out -fPIC -ggdb,$(CXXFLAGS)) EMCCFLAGS := -Os -Wno-warn-absolute-paths EMCCFLAGS += --memory-init-file 0 --embed-file share -s NO_EXIT_RUNTIME=1 @@ -127,12 +156,13 @@ yosys.html: misc/yosys.html else ifeq ($(CONFIG),mxe) CXX = /usr/local/src/mxe/usr/bin/i686-pc-mingw32-gcc +LD = /usr/local/src/mxe/usr/bin/i686-pc-mingw32-gcc CXXFLAGS += -std=gnu++0x -Os -D_POSIX_SOURCE CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s LDLIBS := $(filter-out -lrt,$(LDLIBS)) ABCMKARGS += ARCHFLAGS="-DSIZEOF_VOID_P=4 -DSIZEOF_LONG=4 -DSIZEOF_INT=4 -DWIN32_NO_DLL -x c++ -fpermissive -w" -ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" READLINE=0 CC="$(CXX)" CXX="$(CXX)" +ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" ABC_USE_NO_READLINE=1 CC="$(CXX)" CXX="$(CXX)" EXE = .exe else ifneq ($(CONFIG),none) @@ -179,7 +209,7 @@ endif ifeq ($(ENABLE_VERIFIC),1) VERIFIC_DIR ?= /usr/local/src/verific_lib_eval -VERIFIC_COMPONENTS ?= verilog vhdl database util containers +VERIFIC_COMPONENTS ?= verilog vhdl database util containers sdf CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) endif @@ -300,10 +330,10 @@ yosys.js: $(filter-out yosysjs-$(YOSYS_VER).zip,$(EXTRA_TARGETS)) endif yosys$(EXE): $(OBJS) - $(P) $(CXX) -o yosys$(EXE) $(LDFLAGS) $(OBJS) $(LDLIBS) + $(P) $(LD) -o yosys$(EXE) $(LDFLAGS) $(OBJS) $(LDLIBS) libyosys.so: $(filter-out kernel/driver.o,$(OBJS)) - $(P) $(CXX) -o libyosys.so -shared -Wl,-soname,libyosys.so $(LDFLAGS) $^ $(LDLIBS) + $(P) $(LD) -o libyosys.so -shared -Wl,-soname,libyosys.so $(LDFLAGS) $^ $(LDLIBS) %.o: %.cc $(Q) mkdir -p $(dir $@) @@ -190,15 +190,13 @@ for the given cell library: clean If you do not have a liberty file but want to test this synthesis script, -you can use the file techlibs/cmos/cmos_cells.lib from the yosys sources. +you can use the file examples/cmos/cmos_cells.lib from the yosys sources. -Various more complex liberty files (for testing) can be found here: +Liberty file downloads for and information about free and open ASIC standard +cell libraries can be found here: - http://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/.. - ../cadence/lib/tsmc025/signalstorm/osu025_stdcells.lib - ../cadence/lib/ami035/signalstorm/osu035_stdcells.lib - ../cadence/lib/tsmc018/signalstorm/osu018_stdcells.lib - ../cadence/lib/ami05/signalstorm/osu05_stdcells.lib + http://www.vlsitechnology.org/html/libraries.html + http://www.vlsitechnology.org/synopsys/vsclib013.lib The command "synth" provides a good default synthesis script (see "help synth"). If possible a synthesis script should borrow from "synth". For example: diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc index 0a13180a..f3b57765 100644 --- a/backends/blif/blif.cc +++ b/backends/blif/blif.cc @@ -37,6 +37,7 @@ struct BlifDumperConfig bool conn_mode; bool impltf_mode; bool gates_mode; + bool cname_mode; bool param_mode; bool attr_mode; bool blackbox_mode; @@ -45,7 +46,8 @@ struct BlifDumperConfig std::map<RTLIL::IdString, std::pair<RTLIL::IdString, RTLIL::IdString>> unbuf_types; std::string true_type, true_out, false_type, false_out, undef_type, undef_out; - BlifDumperConfig() : icells_mode(false), conn_mode(false), impltf_mode(false), gates_mode(false), param_mode(false), attr_mode(false), blackbox_mode(false) { } + BlifDumperConfig() : icells_mode(false), conn_mode(false), impltf_mode(false), gates_mode(false), + cname_mode(false), param_mode(false), attr_mode(false), blackbox_mode(false) { } }; struct BlifDumper @@ -349,6 +351,8 @@ struct BlifDumper } f << stringf("\n"); + if (config->cname_mode) + f << stringf(".cname %s\n", cstr(cell->name)); if (config->attr_mode) dump_params(".attr", cell->attributes); if (config->param_mode) @@ -426,6 +430,9 @@ struct BlifBackend : public Backend { log(" -param\n"); log(" use the non-standard .param statement to write cell parameters\n"); log("\n"); + log(" -cname\n"); + log(" use the non-standard .cname statement to write cell names\n"); + log("\n"); log(" -blackbox\n"); log(" write blackbox cells with .blackbox statement.\n"); log("\n"); @@ -490,6 +497,10 @@ struct BlifBackend : public Backend { config.conn_mode = true; continue; } + if (args[argidx] == "-cname") { + config.cname_mode = true; + continue; + } if (args[argidx] == "-param") { config.param_mode = true; continue; diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index 26585f43..465723f1 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -260,7 +260,7 @@ struct BtorDumper if(it==std::end(line_ref)) { ++line_num; - int address_bits = ceil(log(memory->size)/log(2)); + int address_bits = ceil_log2(memory->size); str = stringf("%d array %d %d", line_num, memory->width, address_bits); line_ref[memory->name]=line_num; f << stringf("%s\n", str.c_str()); @@ -272,7 +272,7 @@ struct BtorDumper int dump_memory_next(const RTLIL::Memory* memory) { auto mem_it = line_ref.find(memory->name); - int address_bits = ceil(log(memory->size)/log(2)); + int address_bits = ceil_log2(memory->size); if(mem_it==std::end(line_ref)) { log("can not write next of a memory that is not dumped yet\n"); @@ -593,18 +593,18 @@ struct BtorDumper bool l1_signed = cell->parameters.at(RTLIL::IdString("\\A_SIGNED")).as_bool(); //bool l2_signed = cell->parameters.at(RTLIL::IdString("\\B_SIGNED")).as_bool(); int l1_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int(); - l1_width = pow(2, ceil(log(l1_width)/log(2))); + l1_width = 1 << ceil_log2(l1_width); int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int(); - //log_assert(l2_width <= ceil(log(l1_width)/log(2)) ); + //log_assert(l2_width <= ceil_log2(l1_width)) ); int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), l1_width); - int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), ceil(log(l1_width)/log(2))); + int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), ceil_log2(l1_width)); int cell_output = ++line_num; str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), l1_width, l1, l2); f << stringf("%s\n", str.c_str()); - if(l2_width > ceil(log(l1_width)/log(2))) + if(l2_width > ceil_log2(l1_width)) { - int extra_width = l2_width - ceil(log(l1_width)/log(2)); + int extra_width = l2_width - ceil_log2(l1_width); l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), l2_width); ++line_num; str = stringf ("%d slice %d %d %d %d;6", line_num, extra_width, l2, l2_width-1, l2_width-extra_width); @@ -821,7 +821,7 @@ struct BtorDumper ++line_num; str = cell->parameters.at(RTLIL::IdString("\\MEMID")).decode_string(); RTLIL::Memory *memory = module->memories.at(RTLIL::IdString(str.c_str())); - int address_bits = ceil(log(memory->size)/log(2)); + int address_bits = ceil_log2(memory->size); str = stringf("%d array %d %d", line_num, memory->width, address_bits); f << stringf("%s\n", str.c_str()); ++line_num; diff --git a/backends/smt2/Makefile.inc b/backends/smt2/Makefile.inc index 4e0a393a..eacda273 100644 --- a/backends/smt2/Makefile.inc +++ b/backends/smt2/Makefile.inc @@ -1,3 +1,16 @@ OBJS += backends/smt2/smt2.o +ifneq ($(CONFIG),mxe) +ifneq ($(CONFIG),emcc) +TARGETS += yosys-smtbmc + +yosys-smtbmc: backends/smt2/smtbmc.py + $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(__file__) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new + $(Q) chmod +x $@.new + $(Q) mv $@.new $@ + +$(eval $(call add_share_file,share/python3,backends/smt2/smtio.py)) +endif +endif + diff --git a/backends/smt2/example.v b/backends/smt2/example.v new file mode 100644 index 00000000..b195266e --- /dev/null +++ b/backends/smt2/example.v @@ -0,0 +1,11 @@ +module main(input clk); + reg [3:0] counter = 0; + always @(posedge clk) begin + if (counter == 10) + counter <= 0; + else + counter <= counter + 1; + end + assert property (counter != 15); + // assert property (counter <= 10); +endmodule diff --git a/backends/smt2/example.ys b/backends/smt2/example.ys new file mode 100644 index 00000000..6fccb344 --- /dev/null +++ b/backends/smt2/example.ys @@ -0,0 +1,3 @@ +read_verilog -formal example.v +hierarchy; proc; opt; memory -nordff -nomap; opt -fast +write_smt2 -bv -mem -wires example.smt2 diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index a544aa7d..c852921e 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -32,7 +32,7 @@ struct Smt2Worker CellTypes ct; SigMap sigmap; RTLIL::Module *module; - bool bvmode, memmode, regsmode, verbose; + bool bvmode, memmode, regsmode, wiresmode, verbose; int idcounter; std::vector<std::string> decls, trans; @@ -44,8 +44,9 @@ struct Smt2Worker std::map<Cell*, int> memarrays; std::map<int, int> bvsizes; - Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool regsmode, bool verbose) : - ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode), regsmode(regsmode), verbose(verbose), idcounter(0) + Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool regsmode, bool wiresmode, bool verbose) : + ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode), + regsmode(regsmode), wiresmode(wiresmode), verbose(verbose), idcounter(0) { decls.push_back(stringf("(declare-sort |%s_s| 0)\n", log_id(module))); @@ -132,7 +133,7 @@ struct Smt2Worker std::string get_bool(RTLIL::SigSpec sig, const char *state_name = "state") { - return get_bool(sig.to_single_sigbit(), state_name); + return get_bool(sig.as_bit(), state_name); } std::string get_bv(RTLIL::SigSpec sig, const char *state_name = "state") @@ -215,7 +216,7 @@ struct Smt2Worker void export_gate(RTLIL::Cell *cell, std::string expr) { - RTLIL::SigBit bit = sigmap(cell->getPort("\\Y").to_single_sigbit()); + RTLIL::SigBit bit = sigmap(cell->getPort("\\Y").as_bit()); std::string processed_expr; for (char ch : expr) { @@ -243,8 +244,8 @@ struct Smt2Worker int width = GetSize(sig_y); if (type == 's' || type == 'd' || type == 'b') { - width = std::max(width, GetSize(cell->getPort("\\A"))); - width = std::max(width, GetSize(cell->getPort("\\B"))); + width = max(width, GetSize(cell->getPort("\\A"))); + width = max(width, GetSize(cell->getPort("\\B"))); } if (cell->hasPort("\\A")) { @@ -488,7 +489,7 @@ struct Smt2Worker for (auto bit : SigSpec(wire)) if (reg_bits.count(bit)) is_register = true; - if (wire->port_id || is_register || wire->get_bool_attribute("\\keep")) { + if (wire->port_id || is_register || wire->get_bool_attribute("\\keep") || (wiresmode && wire->name[0] == '\\')) { RTLIL::SigSpec sig = sigmap(wire); if (wire->port_input) decls.push_back(stringf("; yosys-smt2-input %s %d\n", log_id(wire), wire->width)); @@ -496,7 +497,7 @@ struct Smt2Worker decls.push_back(stringf("; yosys-smt2-output %s %d\n", log_id(wire), wire->width)); if (is_register) decls.push_back(stringf("; yosys-smt2-register %s %d\n", log_id(wire), wire->width)); - if (wire->get_bool_attribute("\\keep")) + if (wire->get_bool_attribute("\\keep") || (wiresmode && wire->name[0] == '\\')) decls.push_back(stringf("; yosys-smt2-wire %s %d\n", log_id(wire), wire->width)); if (bvmode && GetSize(sig) > 1) { decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) (_ BitVec %d) %s)\n", @@ -630,6 +631,7 @@ struct Smt2Worker for (auto it : decls) f << it; + f << stringf("; yosys-smt2-module %s\n", log_id(module)); f << stringf("(define-fun |%s_t| ((state |%s_s|) (next_state |%s_s|)) Bool ", log_id(module), log_id(module), log_id(module)); if (GetSize(trans) > 1) { f << "(and\n"; @@ -695,6 +697,9 @@ struct Smt2Backend : public Backend { log(" -regs\n"); log(" also create '<mod>_n' functions for all registers.\n"); log("\n"); + log(" -wires\n"); + log(" also create '<mod>_n' functions for all public wires.\n"); + log("\n"); log(" -tpl <template_file>\n"); log(" use the given template file. the line containing only the token '%%%%'\n"); log(" is replaced with the regular output of this command.\n"); @@ -751,7 +756,7 @@ struct Smt2Backend : public Backend { virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) { std::ifstream template_f; - bool bvmode = false, memmode = false, regsmode = false, verbose = false; + bool bvmode = false, memmode = false, regsmode = false, wiresmode = false, verbose = false; log_header("Executing SMT2 backend.\n"); @@ -777,6 +782,10 @@ struct Smt2Backend : public Backend { regsmode = true; continue; } + if (args[argidx] == "-wires") { + wiresmode = true; + continue; + } if (args[argidx] == "-verbose") { verbose = true; continue; @@ -806,7 +815,7 @@ struct Smt2Backend : public Backend { log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module)); - Smt2Worker worker(module, bvmode, memmode, regsmode, verbose); + Smt2Worker worker(module, bvmode, memmode, regsmode, wiresmode, verbose); worker.run(); worker.write(*f); } diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py new file mode 100644 index 00000000..f2911b3e --- /dev/null +++ b/backends/smt2/smtbmc.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python3 +# +# 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. +# + +import os, sys, getopt, re +##yosys-sys-path## +from smtio import smtio, smtopts, mkvcd + +skip_steps = 0 +step_size = 1 +num_steps = 20 +vcdfile = None +tempind = False +assume_skipped = None +topmod = None +so = smtopts() + + +def usage(): + print(""" +yosys-smtbmc [options] <yosys_smt2_output> + + -t <num_steps>, -t <skip_steps>:<num_steps> + default: skip_steps=0, num_steps=20 + + -u <start_step> + assume asserts in skipped steps in BMC + + -S <step_size> + proof <step_size> time steps at once + + -c <vcd_filename> + write counter-example to this VCD file + (hint: use 'write_smt2 -wires' for maximum + coverage of signals in generated VCD file) + + -i + instead of BMC run temporal induction + + -m <module_name> + name of the top module +""" + so.helpmsg()) + sys.exit(1) + + +try: + opts, args = getopt.getopt(sys.argv[1:], so.optstr + "t:u:S:c:im:") +except: + usage() + +for o, a in opts: + if o == "-t": + match = re.match(r"(\d+):(.*)", a) + if match: + skip_steps = int(match.group(1)) + num_steps = int(match.group(2)) + else: + num_steps = int(a) + elif o == "-u": + assume_skipped = int(a) + elif o == "-S": + step_size = int(a) + elif o == "-c": + vcdfile = a + elif o == "-i": + tempind = True + elif o == "-m": + topmod = a + elif so.handle(o, a): + pass + else: + usage() + +if len(args) != 1: + usage() + + +smt = smtio(opts=so) + +print("%s Solver: %s" % (smt.timestamp(), so.solver)) +smt.setup("QF_AUFBV") + +debug_nets = set() +debug_nets_re = re.compile(r"^; yosys-smt2-(input|output|register|wire) (\S+) (\d+)") + +with open(args[0], "r") as f: + for line in f: + match = debug_nets_re.match(line) + if match: + debug_nets.add(match.group(2)) + if line.startswith("; yosys-smt2-module") and topmod is None: + topmod = line.split()[2] + smt.write(line) + +assert topmod is not None + + +def write_vcd_model(steps): + print("%s Writing model to VCD file." % smt.timestamp()) + + vcd = mkvcd(open(vcdfile, "w")) + for netname in sorted(debug_nets): + width = len(smt.get_net_bin(topmod, netname, "s0")) + vcd.add_net(netname, width) + + for i in range(steps): + vcd.set_time(i) + for netname in debug_nets: + vcd.set_net(netname, smt.get_net_bin(topmod, netname, "s%d" % i)) + + vcd.set_time(steps) + + +if tempind: + retstatus = False + skip_counter = step_size + for step in range(num_steps, -1, -1): + smt.write("(declare-fun s%d () %s_s)" % (step, topmod)) + smt.write("(assert (%s_u s%d))" % (topmod, step)) + + if step == num_steps: + smt.write("(assert (not (%s_a s%d)))" % (topmod, step)) + + else: + smt.write("(assert (%s_t s%d s%d))" % (topmod, step, step+1)) + smt.write("(assert (%s_a s%d))" % (topmod, step)) + + if step > num_steps-skip_steps: + print("%s Skipping induction in step %d.." % (smt.timestamp(), step)) + continue + + skip_counter += 1 + if skip_counter < step_size: + print("%s Skipping induction in step %d.." % (smt.timestamp(), step)) + continue + + skip_counter = 0 + print("%s Trying induction in step %d.." % (smt.timestamp(), step)) + + if smt.check_sat() == "sat": + if step == 0: + print("%s Temporal induction failed!" % smt.timestamp()) + if vcdfile is not None: + write_vcd_model(num_steps+1) + + else: + print("%s Temporal induction successful." % smt.timestamp()) + retstatus = True + break + + +else: # not tempind + step = 0 + retstatus = True + while step < num_steps: + smt.write("(declare-fun s%d () %s_s)" % (step, topmod)) + smt.write("(assert (%s_u s%d))" % (topmod, step)) + + if step == 0: + smt.write("(assert (%s_i s0))" % (topmod)) + + else: + smt.write("(assert (%s_t s%d s%d))" % (topmod, step-1, step)) + + if step < skip_steps: + if assume_skipped is not None and step >= assume_skipped: + print("%s Skipping step %d (and assuming pass).." % (smt.timestamp(), step)) + smt.write("(assert (%s_a s%d))" % (topmod, step)) + else: + print("%s Skipping step %d.." % (smt.timestamp(), step)) + step += 1 + continue + + last_check_step = step + for i in range(1, step_size): + if step+i < num_steps: + smt.write("(declare-fun s%d () %s_s)" % (step+i, topmod)) + smt.write("(assert (%s_u s%d))" % (topmod, step+i)) + smt.write("(assert (%s_t s%d s%d))" % (topmod, step+i-1, step+i)) + last_check_step = step+i + + if last_check_step == step: + print("%s Checking asserts in step %d.." % (smt.timestamp(), step)) + else: + print("%s Checking asserts in steps %d to %d.." % (smt.timestamp(), step, last_check_step)) + smt.write("(push 1)") + + smt.write("(assert (not (and %s)))" % " ".join(["(%s_a s%d)" % (topmod, i) for i in range(step, last_check_step+1)])) + + if smt.check_sat() == "sat": + print("%s BMC failed!" % smt.timestamp()) + if vcdfile is not None: + write_vcd_model(step+step_size) + retstatus = False + break + + else: # unsat + smt.write("(pop 1)") + for i in range(step, last_check_step+1): + smt.write("(assert (%s_a s%d))" % (topmod, i)) + + step += step_size + + +smt.write("(exit)") +smt.wait() + +print("%s Status: %s" % (smt.timestamp(), "PASSED" if retstatus else "FAILED (!)")) +sys.exit(0 if retstatus else 1) + diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py new file mode 100644 index 00000000..6e8bded7 --- /dev/null +++ b/backends/smt2/smtio.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python3 +# +# 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. +# + +import sys +import subprocess +from select import select +from time import time + +class smtio: + def __init__(self, solver=None, debug_print=None, debug_file=None, timeinfo=None, opts=None): + if opts is not None: + self.solver = opts.solver + self.debug_print = opts.debug_print + self.debug_file = opts.debug_file + self.timeinfo = opts.timeinfo + + else: + self.solver = "z3" + self.debug_print = False + self.debug_file = None + self.timeinfo = True + + if solver is not None: + self.solver = solver + + if debug_print is not None: + self.debug_print = debug_print + + if debug_file is not None: + self.debug_file = debug_file + + if timeinfo is not None: + self.timeinfo = timeinfo + + if self.solver == "yices": + popen_vargs = ['yices-smt2', '--incremental'] + + if self.solver == "z3": + popen_vargs = ['z3', '-smt2', '-in'] + + if self.solver == "cvc4": + popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2'] + + if self.solver == "mathsat": + popen_vargs = ['mathsat'] + + self.p = subprocess.Popen(popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + self.start_time = time() + + def setup(self, logic="ALL", info=None): + self.write("(set-logic %s)" % logic) + if info is not None: + self.write("(set-info :source |%s|)" % info) + self.write("(set-info :smt-lib-version 2.5)") + self.write("(set-info :category \"industrial\")") + + def timestamp(self): + secs = int(time() - self.start_time) + return "## %6d %3d:%02d:%02d " % (secs, secs // (60*60), (secs // 60) % 60, secs % 60) + + def write(self, stmt): + stmt = stmt.strip() + if self.debug_print: + print("> %s" % stmt) + if self.debug_file: + print(stmt, file=self.debug_file) + self.debug_file.flush() + self.p.stdin.write(bytes(stmt + "\n", "ascii")) + self.p.stdin.flush() + + def read(self): + stmt = [] + count_brackets = 0 + + while True: + line = self.p.stdout.readline().decode("ascii").strip() + count_brackets += line.count("(") + count_brackets -= line.count(")") + stmt.append(line) + if self.debug_print: + print("< %s" % line) + if count_brackets == 0: + break + if not self.p.poll(): + print("SMT Solver terminated unexpectedly: %s" % "".join(stmt)) + sys.exit(1) + + stmt = "".join(stmt) + if stmt.startswith("(error"): + print("SMT Solver Error: %s" % stmt, file=sys.stderr) + sys.exit(1) + + return stmt + + def check_sat(self): + if self.debug_print: + print("> (check-sat)") + if self.debug_file: + print("; running check-sat..", file=self.debug_file) + self.debug_file.flush() + + self.p.stdin.write(bytes("(check-sat)\n", "ascii")) + self.p.stdin.flush() + + if self.timeinfo: + i = 0 + s = "/-\|" + + count = 0 + num_bs = 0 + while select([self.p.stdout], [], [], 0.1) == ([], [], []): + count += 1 + + if count < 25: + continue + + if count % 10 == 0 or count == 25: + secs = count // 10 + + if secs < 60: + m = "(%d seconds)" % secs + elif secs < 60*60: + m = "(%d seconds -- %d:%02d)" % (secs, secs // 60, secs % 60) + else: + m = "(%d seconds -- %d:%02d:%02d)" % (secs, secs // (60*60), (secs // 60) % 60, secs % 60) + + print("%s %s %c" % ("\b \b" * num_bs, m, s[i]), end="", file=sys.stderr) + num_bs = len(m) + 3 + + else: + print("\b" + s[i], end="", file=sys.stderr) + + sys.stderr.flush() + i = (i + 1) % len(s) + + if num_bs != 0: + print("\b \b" * num_bs, end="", file=sys.stderr) + sys.stderr.flush() + + result = self.read() + if self.debug_file: + print("(set-info :status %s)" % result, file=self.debug_file) + print("(check-sat)", file=self.debug_file) + self.debug_file.flush() + return result + + def parse(self, stmt): + def worker(stmt): + if stmt[0] == '(': + expr = [] + cursor = 1 + while stmt[cursor] != ')': + el, le = worker(stmt[cursor:]) + expr.append(el) + cursor += le + return expr, cursor+1 + + if stmt[0] == '|': + expr = "|" + cursor = 1 + while stmt[cursor] != '|': + expr += stmt[cursor] + cursor += 1 + expr += "|" + return expr, cursor+1 + + if stmt[0] in [" ", "\t", "\r", "\n"]: + el, le = worker(stmt[1:]) + return el, le+1 + + expr = "" + cursor = 0 + while stmt[cursor] not in ["(", ")", "|", " ", "\t", "\r", "\n"]: + expr += stmt[cursor] + cursor += 1 + return expr, cursor + return worker(stmt)[0] + + def bv2hex(self, v): + h = "" + v = bv2bin(v) + while len(v) > 0: + d = 0 + if len(v) > 0 and v[-1] == "1": d += 1 + if len(v) > 1 and v[-2] == "1": d += 2 + if len(v) > 2 and v[-3] == "1": d += 4 + if len(v) > 3 and v[-4] == "1": d += 8 + h = hex(d)[2:] + h + if len(v) < 4: break + v = v[:-4] + return h + + def bv2bin(self, v): + if v == "true": return "1" + if v == "false": return "0" + if v.startswith("#b"): + return v[2:] + if v.startswith("#x"): + digits = [] + for d in v[2:]: + if d == "0": digits.append("0000") + if d == "1": digits.append("0001") + if d == "2": digits.append("0010") + if d == "3": digits.append("0011") + if d == "4": digits.append("0100") + if d == "5": digits.append("0101") + if d == "6": digits.append("0110") + if d == "7": digits.append("0111") + if d == "8": digits.append("1000") + if d == "9": digits.append("1001") + if d in ("a", "A"): digits.append("1010") + if d in ("b", "B"): digits.append("1011") + if d in ("c", "C"): digits.append("1100") + if d in ("d", "D"): digits.append("1101") + if d in ("e", "E"): digits.append("1110") + if d in ("f", "F"): digits.append("1111") + return "".join(digits) + assert False + + def get(self, expr): + self.write("(get-value (%s))" % (expr)) + return self.parse(self.read())[0][1] + + def get_net(self, mod_name, net_name, state_name): + return self.get("(|%s_n %s| %s)" % (mod_name, net_name, state_name)) + + def get_net_bool(self, mod_name, net_name, state_name): + v = self.get_net(mod_name, net_name, state_name) + assert v in ["true", "false"] + return 1 if v == "true" else 0 + + def get_net_hex(self, mod_name, net_name, state_name): + return self.bv2hex(self.get_net(mod_name, net_name, state_name)) + + def get_net_bin(self, mod_name, net_name, state_name): + return self.bv2bin(self.get_net(mod_name, net_name, state_name)) + + def wait(self): + self.p.wait() + + +class smtopts: + def __init__(self): + self.optstr = "s:d:vp" + self.solver = "z3" + self.debug_print = False + self.debug_file = None + self.timeinfo = True + + def handle(self, o, a): + if o == "-s": + self.solver = a + elif o == "-v": + self.debug_print = True + elif o == "-p": + self.timeinfo = True + elif o == "-d": + self.debug_file = open(a, "w") + else: + return False + return True + + def helpmsg(self): + return """ + -s <solver> + set SMT solver: z3, cvc4, yices, mathsat + default: z3 + + -v + enable debug output + + -p + disable timer display during solving + + -d <filename> + write smt2 statements to file +""" + + +class mkvcd: + def __init__(self, f): + self.f = f + self.t = -1 + self.nets = dict() + + def add_net(self, name, width): + assert self.t == -1 + key = "n%d" % len(self.nets) + self.nets[name] = (key, width) + + def set_net(self, name, bits): + assert name in self.nets + assert self.t >= 0 + print("b%s %s" % (bits, self.nets[name][0]), file=self.f) + + def set_time(self, t): + assert t >= self.t + if t != self.t: + if self.t == -1: + print("$var event 1 ! smt_clock $end", file=self.f) + for name in sorted(self.nets): + key, width = self.nets[name] + print("$var wire %d %s %s $end" % (width, key, name), file=self.f) + print("$enddefinitions $end", file=self.f) + self.t = t + assert self.t >= 0 + print("#%d" % self.t, file=self.f) + print("1!", file=self.f) + diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc index fdf022c5..b29a88ac 100644 --- a/backends/smv/smv.cc +++ b/backends/smv/smv.cc @@ -244,13 +244,13 @@ struct SmvWorker int width_y = GetSize(cell->getPort("\\Y")); int shift_b_width = GetSize(sig_b); - int width_ay = std::max(GetSize(sig_a), width_y); + int width_ay = max(GetSize(sig_a), width_y); int width = width_ay; for (int i = 1, j = 0;; i <<= 1, j++) if (width_ay < i) { width = i-1; - shift_b_width = std::min(shift_b_width, j); + shift_b_width = min(shift_b_width, j); break; } @@ -361,8 +361,8 @@ struct SmvWorker if (cell->type.in("$div", "$mod")) { int width_y = GetSize(cell->getPort("\\Y")); - int width = std::max(width_y, GetSize(cell->getPort("\\A"))); - width = std::max(width, GetSize(cell->getPort("\\B"))); + int width = max(width_y, GetSize(cell->getPort("\\A"))); + width = max(width, GetSize(cell->getPort("\\B"))); string expr_a, expr_b, op; if (cell->type == "$div") op = "/"; @@ -384,7 +384,7 @@ struct SmvWorker if (cell->type.in("$eq", "$ne", "$eqx", "$nex", "$lt", "$le", "$ge", "$gt")) { - int width = std::max(GetSize(cell->getPort("\\A")), GetSize(cell->getPort("\\B"))); + int width = max(GetSize(cell->getPort("\\A")), GetSize(cell->getPort("\\B"))); string expr_a, expr_b, op; if (cell->type == "$eq") op = "="; diff --git a/techlibs/xilinx/example_basys3/README b/examples/basys3/README index 85b6eab1..0ce71729 100644 --- a/techlibs/xilinx/example_basys3/README +++ b/examples/basys3/README @@ -2,6 +2,9 @@ A simple example design, based on the Digilent BASYS3 board =========================================================== +This example uses Yosys for synthesis and Xilinx Vivado +for place&route and bit-stream creation. + Running Yosys: yosys run_yosys.ys diff --git a/techlibs/xilinx/example_basys3/example.v b/examples/basys3/example.v index 2b01a22a..2b01a22a 100644 --- a/techlibs/xilinx/example_basys3/example.v +++ b/examples/basys3/example.v diff --git a/techlibs/xilinx/example_basys3/example.xdc b/examples/basys3/example.xdc index c1fd0e92..c1fd0e92 100644 --- a/techlibs/xilinx/example_basys3/example.xdc +++ b/examples/basys3/example.xdc diff --git a/techlibs/xilinx/example_basys3/run.sh b/examples/basys3/run.sh index 10f05910..10f05910 100644 --- a/techlibs/xilinx/example_basys3/run.sh +++ b/examples/basys3/run.sh diff --git a/techlibs/xilinx/example_basys3/run_prog.tcl b/examples/basys3/run_prog.tcl index d711af84..d711af84 100644 --- a/techlibs/xilinx/example_basys3/run_prog.tcl +++ b/examples/basys3/run_prog.tcl diff --git a/techlibs/xilinx/example_basys3/run_vivado.tcl b/examples/basys3/run_vivado.tcl index c3b6a610..c3b6a610 100644 --- a/techlibs/xilinx/example_basys3/run_vivado.tcl +++ b/examples/basys3/run_vivado.tcl diff --git a/techlibs/xilinx/example_basys3/run_yosys.ys b/examples/basys3/run_yosys.ys index 4541826d..4541826d 100644 --- a/techlibs/xilinx/example_basys3/run_yosys.ys +++ b/examples/basys3/run_yosys.ys diff --git a/techlibs/cmos/cmos_cells.lib b/examples/cmos/cmos_cells.lib index 1b0bf845..1b0bf845 100644 --- a/techlibs/cmos/cmos_cells.lib +++ b/examples/cmos/cmos_cells.lib diff --git a/techlibs/cmos/cmos_cells.sp b/examples/cmos/cmos_cells.sp index 673b20d0..673b20d0 100644 --- a/techlibs/cmos/cmos_cells.sp +++ b/examples/cmos/cmos_cells.sp diff --git a/techlibs/cmos/cmos_cells.v b/examples/cmos/cmos_cells.v index 27278fac..27278fac 100644 --- a/techlibs/cmos/cmos_cells.v +++ b/examples/cmos/cmos_cells.v diff --git a/techlibs/cmos/counter.v b/examples/cmos/counter.v index f2165872..f2165872 100644 --- a/techlibs/cmos/counter.v +++ b/examples/cmos/counter.v diff --git a/techlibs/cmos/counter.ys b/examples/cmos/counter.ys index a784f346..a784f346 100644 --- a/techlibs/cmos/counter.ys +++ b/examples/cmos/counter.ys diff --git a/techlibs/cmos/testbench.sh b/examples/cmos/testbench.sh index 061704b6..061704b6 100644 --- a/techlibs/cmos/testbench.sh +++ b/examples/cmos/testbench.sh diff --git a/techlibs/cmos/testbench.sp b/examples/cmos/testbench.sp index 95d2f67c..95d2f67c 100644 --- a/techlibs/cmos/testbench.sp +++ b/examples/cmos/testbench.sp diff --git a/misc/example.cc b/examples/cxx-api/demomain.cc index 2e35bcd4..a6459330 100644 --- a/misc/example.cc +++ b/examples/cxx-api/demomain.cc @@ -1,5 +1,5 @@ // Note: Set ENABLE_LIBYOSYS=1 in Makefile or Makefile.conf to build libyosys.so -// yosys-config --exec --cxx -o example --cxxflags --ldflags example.cc -lyosys -lstdc++ +// yosys-config --exec --cxx -o demomain --cxxflags --ldflags demomain.cc -lyosys -lstdc++ #include <kernel/yosys.h> diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 3e163bae..834ee82a 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -820,7 +820,7 @@ uint64_t AstNode::asInt(bool is_signed) } if (type == AST_REALVALUE) - return realvalue; + return uint64_t(realvalue); log_abort(); } diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index c322faf7..9fc59037 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -379,7 +379,7 @@ struct AST_INTERNAL::ProcessGenerator // e.g. when the last statement in the code "a = 23; if (b) a = 42; a = 0;" is processed this // function is called to clean up the first two assignments as they are overwritten by // the third assignment. - void removeSignalFromCaseTree(const std::set<RTLIL::SigBit> &pattern, RTLIL::CaseRule *cs) + void removeSignalFromCaseTree(const RTLIL::SigSpec &pattern, RTLIL::CaseRule *cs) { for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) it->first.remove2(pattern, &it->second); @@ -434,7 +434,7 @@ struct AST_INTERNAL::ProcessGenerator subst_rvalue_map.set(unmapped_lvalue[i], rvalue[i]); } - removeSignalFromCaseTree(lvalue.to_sigbit_set(), current_case); + removeSignalFromCaseTree(lvalue, current_case); remove_unwanted_lvalue_bits(lvalue, rvalue); current_case->actions.push_back(RTLIL::SigSig(lvalue, rvalue)); } @@ -511,7 +511,7 @@ struct AST_INTERNAL::ProcessGenerator subst_rvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]); this_case_eq_lvalue.replace(subst_lvalue_map.stdmap()); - removeSignalFromCaseTree(this_case_eq_lvalue.to_sigbit_set(), current_case); + removeSignalFromCaseTree(this_case_eq_lvalue, current_case); addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp); } break; @@ -520,6 +520,11 @@ struct AST_INTERNAL::ProcessGenerator log_error("Found wire declaration in block without label at at %s:%d!\n", ast->filename.c_str(), ast->linenum); break; + case AST_PARAMETER: + case AST_LOCALPARAM: + log_error("Found parameter declaration in block without label at at %s:%d!\n", ast->filename.c_str(), ast->linenum); + break; + case AST_TCALL: case AST_FOR: break; @@ -547,14 +552,14 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun switch (type) { case AST_CONSTANT: - width_hint = std::max(width_hint, int(bits.size())); + width_hint = max(width_hint, int(bits.size())); if (!is_signed) sign_hint = false; break; case AST_REALVALUE: *found_real = true; - width_hint = std::max(width_hint, 32); + width_hint = max(width_hint, 32); break; case AST_IDENTIFIER: @@ -617,7 +622,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun this_width = range->range_left - range->range_right + 1; sign_hint = false; } - width_hint = std::max(width_hint, this_width); + width_hint = max(width_hint, this_width); if (!id_ast->is_signed) sign_hint = false; break; @@ -627,7 +632,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (children[0]->type != AST_CONSTANT) log_error("Left operand of tobits expression is not constant at %s:%d!\n", filename.c_str(), linenum); children[1]->detectSignWidthWorker(sub_width_hint, sign_hint); - width_hint = std::max(width_hint, children[0]->bitsAsConst().as_int()); + width_hint = max(width_hint, children[0]->bitsAsConst().as_int()); break; case AST_TO_SIGNED: @@ -646,7 +651,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun child->detectSignWidthWorker(sub_width_hint, sub_sign_hint); this_width += sub_width_hint; } - width_hint = std::max(width_hint, this_width); + width_hint = max(width_hint, this_width); sign_hint = false; break; @@ -655,7 +660,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (children[0]->type != AST_CONSTANT) log_error("Left operand of replicate expression is not constant at %s:%d!\n", filename.c_str(), linenum); children[1]->detectSignWidthWorker(sub_width_hint, sub_sign_hint); - width_hint = std::max(width_hint, children[0]->bitsAsConst().as_int() * sub_width_hint); + width_hint = max(width_hint, children[0]->bitsAsConst().as_int() * sub_width_hint); sign_hint = false; break; @@ -678,7 +683,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_REDUCE_XOR: case AST_REDUCE_XNOR: case AST_REDUCE_BOOL: - width_hint = std::max(width_hint, 1); + width_hint = max(width_hint, 1); sign_hint = false; break; @@ -698,7 +703,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_NEX: case AST_GE: case AST_GT: - width_hint = std::max(width_hint, 1); + width_hint = max(width_hint, 1); sign_hint = false; break; @@ -714,7 +719,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_LOGIC_AND: case AST_LOGIC_OR: case AST_LOGIC_NOT: - width_hint = std::max(width_hint, 1); + width_hint = max(width_hint, 1); sign_hint = false; break; @@ -729,7 +734,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (!id2ast->children[0]->range_valid) log_error("Failed to detect with of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); this_width = id2ast->children[0]->range_left - id2ast->children[0]->range_right + 1; - width_hint = std::max(width_hint, this_width); + width_hint = max(width_hint, this_width); break; // everything should have been handled above -> print error if not. @@ -1054,7 +1059,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) detectSignWidth(width_hint, sign_hint); RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint); RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint); - int width = std::max(left.size(), right.size()); + int width = max(left.size(), right.size()); if (width_hint > 0) width = width_hint; is_signed = children[0]->is_signed && children[1]->is_signed; @@ -1068,7 +1073,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (0) { case AST_REDUCE_XNOR: type_name = "$reduce_xnor"; } { RTLIL::SigSpec arg = children[0]->genRTLIL(); - RTLIL::SigSpec sig = uniop2rtlil(this, type_name, std::max(width_hint, 1), arg); + RTLIL::SigSpec sig = uniop2rtlil(this, type_name, max(width_hint, 1), arg); return sig; } @@ -1077,7 +1082,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (0) { case AST_REDUCE_BOOL: type_name = "$reduce_bool"; } { RTLIL::SigSpec arg = children[0]->genRTLIL(); - RTLIL::SigSpec sig = arg.size() > 1 ? uniop2rtlil(this, type_name, std::max(width_hint, 1), arg) : arg; + RTLIL::SigSpec sig = arg.size() > 1 ? uniop2rtlil(this, type_name, max(width_hint, 1), arg) : arg; return sig; } @@ -1123,7 +1128,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (0) { case AST_GE: type_name = "$ge"; } if (0) { case AST_GT: type_name = "$gt"; } { - int width = std::max(width_hint, 1); + int width = max(width_hint, 1); width_hint = -1, sign_hint = true; children[0]->detectSignWidthWorker(width_hint, sign_hint); children[1]->detectSignWidthWorker(width_hint, sign_hint); @@ -1145,7 +1150,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint); RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint); #if 0 - int width = std::max(left.size(), right.size()); + int width = max(left.size(), right.size()); if (width > width_hint && width_hint > 0) width = width_hint; if (width < width_hint) { @@ -1154,10 +1159,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (type == AST_SUB && (!children[0]->is_signed || !children[1]->is_signed)) width = width_hint; if (type == AST_MUL) - width = std::min(left.size() + right.size(), width_hint); + width = min(left.size() + right.size(), width_hint); } #else - int width = std::max(std::max(left.size(), right.size()), width_hint); + int width = max(max(left.size(), right.size()), width_hint); #endif is_signed = children[0]->is_signed && children[1]->is_signed; return binop2rtlil(this, type_name, width, left, right); @@ -1169,14 +1174,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) { RTLIL::SigSpec left = children[0]->genRTLIL(); RTLIL::SigSpec right = children[1]->genRTLIL(); - return binop2rtlil(this, type_name, std::max(width_hint, 1), left, right); + return binop2rtlil(this, type_name, max(width_hint, 1), left, right); } // generate cells for unary operations: $logic_not case AST_LOGIC_NOT: { RTLIL::SigSpec arg = children[0]->genRTLIL(); - return uniop2rtlil(this, "$logic_not", std::max(width_hint, 1), arg); + return uniop2rtlil(this, "$logic_not", max(width_hint, 1), arg); } // generate multiplexer for ternary operator (aka ?:-operator) @@ -1192,7 +1197,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (cond.size() > 1) cond = uniop2rtlil(this, "$reduce_bool", 1, cond, false); - int width = std::max(val1.size(), val2.size()); + int width = max(val1.size(), val2.size()); is_signed = children[1]->is_signed && children[2]->is_signed; widthExtend(this, val1, width, is_signed); widthExtend(this, val2, width, is_signed); @@ -1252,7 +1257,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (type == AST_MEMINIT) { if (children[2]->type != AST_CONSTANT) log_error("Memory init with non-constant word count at %s:%d!\n", filename.c_str(), linenum); - num_words = children[2]->asInt(false); + num_words = int(children[2]->asInt(false)); cell->parameters["\\WORDS"] = RTLIL::Const(num_words); } diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 861c3bcc..2621be49 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -398,7 +398,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, did_something = true; children[0]->detectSignWidth(backup_width_hint, backup_sign_hint); children[1]->detectSignWidth(width_hint, sign_hint); - width_hint = std::max(width_hint, backup_width_hint); + width_hint = max(width_hint, backup_width_hint); child_0_is_self_determined = true; break; @@ -412,7 +412,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, did_something = true; if (!children[1]->range_valid) log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum); - width_hint = std::max(width_hint, children[1]->range_left - children[1]->range_right + 1); + width_hint = max(width_hint, children[1]->range_left - children[1]->range_right + 1); } break; @@ -733,8 +733,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, for (auto range : children[1]->children) { if (!range->range_valid) log_error("Non-constant range on memory decl at %s:%d.\n", filename.c_str(), linenum); - multirange_dimensions.push_back(std::min(range->range_left, range->range_right)); - multirange_dimensions.push_back(std::max(range->range_left, range->range_right) - std::min(range->range_left, range->range_right) + 1); + multirange_dimensions.push_back(min(range->range_left, range->range_right)); + multirange_dimensions.push_back(max(range->range_left, range->range_right) - min(range->range_left, range->range_right) + 1); total_size *= multirange_dimensions.back(); } delete children[1]; @@ -1013,7 +1013,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, std::vector<AstNode*> new_children; for (size_t i = 0; i < children.size(); i++) - if (children[i]->type == AST_WIRE) { + if (children[i]->type == AST_WIRE || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM) { children[i]->simplify(false, false, false, stage, -1, false, false); current_ast_mod->children.push_back(children[i]); current_scope[children[i]->str] = children[i]; @@ -1169,7 +1169,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, log_error("Non-constant array range on cell array at %s:%d.\n", filename.c_str(), linenum); newNode = new AstNode(AST_GENBLOCK); - int num = std::max(children.at(0)->range_left, children.at(0)->range_right) - std::min(children.at(0)->range_left, children.at(0)->range_right) + 1; + int num = max(children.at(0)->range_left, children.at(0)->range_right) - min(children.at(0)->range_left, children.at(0)->range_right) + 1; for (int i = 0; i < num; i++) { int idx = children.at(0)->range_left > children.at(0)->range_right ? children.at(0)->range_right + i : children.at(0)->range_right - i; @@ -1705,7 +1705,7 @@ skip_dynamic_range_lvalue_expansion:; while (node_addr->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } if (node_addr->type != AST_CONSTANT) log_error("Failed to evaluate system function `%s' with non-constant 3rd argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); - start_addr = node_addr->asInt(false); + start_addr = int(node_addr->asInt(false)); } if (GetSize(children) > 3) { @@ -1713,7 +1713,7 @@ skip_dynamic_range_lvalue_expansion:; while (node_addr->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } if (node_addr->type != AST_CONSTANT) log_error("Failed to evaluate system function `%s' with non-constant 4th argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); - finish_addr = node_addr->asInt(false); + finish_addr = int(node_addr->asInt(false)); } bool unconditional_init = false; @@ -1773,6 +1773,8 @@ skip_dynamic_range_lvalue_expansion:; size_t arg_count = 0; std::map<std::string, std::string> replace_rules; + vector<AstNode*> added_mod_children; + dict<std::string, AstNode*> wire_cache; if (current_block == NULL) { @@ -1865,17 +1867,39 @@ skip_dynamic_range_lvalue_expansion:; } for (auto child : decl->children) - if (child->type == AST_WIRE) + if (child->type == AST_WIRE || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM) { - AstNode *wire = child->clone(); - wire->str = prefix + wire->str; - wire->port_id = 0; - wire->is_input = false; - wire->is_output = false; - current_ast_mod->children.push_back(wire); - while (wire->simplify(true, false, false, 1, -1, false, false)) { } + AstNode *wire = nullptr; + + if (wire_cache.count(child->str)) + { + wire = wire_cache.at(child->str); + if (wire->children.empty()) { + for (auto c : child->children) + wire->children.push_back(c->clone()); + } else { + if (!child->children.empty()) + log_error("Incompatible re-declaration of wire %s at %s:%d.\n", child->str.c_str(), filename.c_str(), linenum); + } + } + else + { + wire = child->clone(); + wire->str = prefix + wire->str; + wire->port_id = 0; + wire->is_input = false; + wire->is_output = false; + wire_cache[child->str] = wire; + + current_ast_mod->children.push_back(wire); + added_mod_children.push_back(wire); + } + + if (child->type == AST_WIRE) + while (wire->simplify(true, false, false, 1, -1, false, false)) { } replace_rules[child->str] = wire->str; + current_scope[wire->str] = wire; if ((child->is_input || child->is_output) && arg_count < children.size()) { @@ -1895,8 +1919,13 @@ skip_dynamic_range_lvalue_expansion:; } } + for (auto child : added_mod_children) { + child->replace_ids(prefix, replace_rules); + while (child->simplify(true, false, false, 1, -1, false, false)) { } + } + for (auto child : decl->children) - if (child->type != AST_WIRE) + if (child->type != AST_WIRE && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM) { AstNode *stmt = child->clone(); stmt->replace_ids(prefix, replace_rules); @@ -2043,7 +2072,7 @@ skip_dynamic_range_lvalue_expansion:; if (0) { case AST_GE: const_func = RTLIL::const_ge; } if (0) { case AST_GT: const_func = RTLIL::const_gt; } if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { - int cmp_width = std::max(children[0]->bits.size(), children[1]->bits.size()); + int cmp_width = max(children[0]->bits.size(), children[1]->bits.size()); bool cmp_signed = children[0]->is_signed && children[1]->is_signed; RTLIL::Const y = const_func(children[0]->bitsAsConst(cmp_width, cmp_signed), children[1]->bitsAsConst(cmp_width, cmp_signed), cmp_signed, cmp_signed, 1); @@ -2236,7 +2265,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m log_assert(GetSize(memory->children) == 2 && memory->children[1]->type == AST_RANGE && memory->children[1]->range_valid); int range_left = memory->children[1]->range_left, range_right = memory->children[1]->range_right; - int range_min = std::min(range_left, range_right), range_max = std::max(range_left, range_right); + int range_min = min(range_left, range_right), range_max = max(range_left, range_right); if (start_addr < 0) start_addr = range_min; @@ -2720,7 +2749,7 @@ void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits) if (mem_size < 0) mem_size *= -1; - mem_size += std::min(children[1]->range_left, children[1]->range_right) + 1; + mem_size += min(children[1]->range_left, children[1]->range_right) + 1; addr_bits = 1; while ((1 << addr_bits) < mem_size) @@ -2756,8 +2785,8 @@ void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &varia if (!children.at(0)->range_valid) log_error("Non-constant range in %s:%d (called from %s:%d).\n", filename.c_str(), linenum, fcall->filename.c_str(), fcall->linenum); - offset = std::min(children.at(0)->range_left, children.at(0)->range_right); - width = std::min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width); + offset = min(children.at(0)->range_left, children.at(0)->range_right); + width = min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width); } offset -= variables.at(str).offset; std::vector<RTLIL::State> &var_bits = variables.at(str).val.bits; @@ -2797,7 +2826,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) log_error("Can't determine size of variable %s in %s:%d (called from %s:%d).\n", child->str.c_str(), child->filename.c_str(), child->linenum, fcall->filename.c_str(), fcall->linenum); variables[child->str].val = RTLIL::Const(RTLIL::State::Sx, abs(child->range_left - child->range_right)+1); - variables[child->str].offset = std::min(child->range_left, child->range_right); + variables[child->str].offset = min(child->range_left, child->range_right); variables[child->str].is_signed = child->is_signed; if (child->is_input && argidx < fcall->children.size()) variables[child->str].val = fcall->children.at(argidx++)->bitsAsConst(variables[child->str].val.bits.size()); @@ -2856,7 +2885,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (!range->range_valid) log_error("Non-constant range in %s:%d (called from %s:%d).\n", range->filename.c_str(), range->linenum, fcall->filename.c_str(), fcall->linenum); - int offset = std::min(range->range_left, range->range_right); + int offset = min(range->range_left, range->range_right); int width = std::abs(range->range_left - range->range_right) + 1; varinfo_t &v = variables[stmt->children.at(0)->str]; RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.bits.size()); diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc index 9f2e08df..ee0e771e 100644 --- a/frontends/blif/blifparse.cc +++ b/frontends/blif/blifparse.cc @@ -49,11 +49,42 @@ static bool read_next_line(char *&buffer, size_t &buffer_size, int &line_count, } } -void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name) +void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean) { RTLIL::Module *module = nullptr; RTLIL::Const *lutptr = NULL; RTLIL::State lut_default_state = RTLIL::State::Sx; + int blif_maxnum = 0; + + auto blif_wire = [&](const std::string &wire_name) -> Wire* + { + if (wire_name[0] == '$') + { + for (int i = 0; i+1 < GetSize(wire_name); i++) + { + if (wire_name[i] != '$') + continue; + + int len = 0; + while (i+len+1 < GetSize(wire_name) && '0' <= wire_name[i+len+1] && wire_name[i+len+1] <= '9') + len++; + + if (len > 0) { + string num_str = wire_name.substr(i+1, len); + int num = atoi(num_str.c_str()) & 0x0fffffff; + blif_maxnum = std::max(blif_maxnum, num); + } + } + } + + IdString wire_id = RTLIL::escape_id(wire_name); + Wire *wire = module->wire(wire_id); + + if (wire == nullptr) + wire = module->addWire(wire_id); + + return wire; + }; dict<RTLIL::IdString, RTLIL::Const> *obj_attributes = nullptr; dict<RTLIL::IdString, RTLIL::Const> *obj_parameters = nullptr; @@ -103,8 +134,41 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name) if (module == nullptr) goto error; - if (!strcmp(cmd, ".end")) { + if (!strcmp(cmd, ".end")) + { module->fixup_ports(); + + if (run_clean) + { + Const buffer_lut(vector<RTLIL::State>({State::S0, State::S1})); + vector<Cell*> remove_cells; + + for (auto cell : module->cells()) + if (cell->type == "$lut" && cell->getParam("\\LUT") == buffer_lut) { + module->connect(cell->getPort("\\Y"), cell->getPort("\\A")); + remove_cells.push_back(cell); + } + + for (auto cell : remove_cells) + module->remove(cell); + + Wire *true_wire = module->wire("$true"); + Wire *false_wire = module->wire("$false"); + Wire *undef_wire = module->wire("$undef"); + + if (true_wire != nullptr) + module->rename(true_wire, stringf("$true$%d", ++blif_maxnum)); + + if (false_wire != nullptr) + module->rename(false_wire, stringf("$false$%d", ++blif_maxnum)); + + if (undef_wire != nullptr) + module->rename(undef_wire, stringf("$undef$%d", ++blif_maxnum)); + + autoidx = std::max(autoidx, blif_maxnum+1); + blif_maxnum = 0; + } + module = nullptr; obj_attributes = nullptr; obj_parameters = nullptr; @@ -114,7 +178,10 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name) if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs")) { char *p; while ((p = strtok(NULL, " \t\r\n")) != NULL) { - RTLIL::Wire *wire = module->addWire(stringf("\\%s", p)); + RTLIL::IdString wire_name(stringf("\\%s", p)); + RTLIL::Wire *wire = module->wire(wire_name); + if (wire == nullptr) + wire = module->addWire(wire_name); if (!strcmp(cmd, ".inputs")) wire->port_input = true; else @@ -162,37 +229,26 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name) char *init = strtok(NULL, " \t\r\n"); RTLIL::Cell *cell = nullptr; - if (module->wires_.count(RTLIL::escape_id(d)) == 0) - module->addWire(RTLIL::escape_id(d)); - - if (module->wires_.count(RTLIL::escape_id(q)) == 0) - module->addWire(RTLIL::escape_id(q)); - if (clock == nullptr && edge != nullptr) { init = edge; edge = nullptr; } if (init != nullptr && (init[0] == '0' || init[0] == '1')) - module->wire(RTLIL::escape_id(d))->attributes["\\init"] = Const(init[0] == '1' ? 1 : 0, 1); + blif_wire(d)->attributes["\\init"] = Const(init[0] == '1' ? 1 : 0, 1); if (clock == nullptr) goto no_latch_clock; - if (module->wires_.count(RTLIL::escape_id(clock)) == 0) - module->addWire(RTLIL::escape_id(clock)); - if (!strcmp(edge, "re")) - cell = module->addDff(NEW_ID, module->wire(RTLIL::escape_id(clock)), - module->wire(RTLIL::escape_id(d)), module->wire(RTLIL::escape_id(q))); + cell = module->addDff(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q)); else if (!strcmp(edge, "fe")) - cell = module->addDff(NEW_ID, module->wire(RTLIL::escape_id(clock)), - module->wire(RTLIL::escape_id(d)), module->wire(RTLIL::escape_id(q)), false); + cell = module->addDff(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q), false); else { no_latch_clock: cell = module->addCell(NEW_ID, dff_name); - cell->setPort("\\D", module->wires_.at(RTLIL::escape_id(d))); - cell->setPort("\\Q", module->wires_.at(RTLIL::escape_id(q))); + cell->setPort("\\D", blif_wire(d)); + cell->setPort("\\Q", blif_wire(q)); } obj_attributes = &cell->attributes; @@ -211,12 +267,10 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name) while ((p = strtok(NULL, " \t\r\n")) != NULL) { char *q = strchr(p, '='); - if (q == NULL || !q[0] || !q[1]) + if (q == NULL || !q[0]) goto error; *(q++) = 0; - if (module->wires_.count(RTLIL::escape_id(q)) == 0) - module->addWire(RTLIL::escape_id(q)); - cell->setPort(RTLIL::escape_id(p), module->wires_.at(RTLIL::escape_id(q))); + cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec()); } obj_attributes = &cell->attributes; @@ -237,13 +291,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name) if (q == NULL) goto error; - if (module->wires_.count(RTLIL::escape_id(p)) == 0) - module->addWire(RTLIL::escape_id(p)); - - if (module->wires_.count(RTLIL::escape_id(q)) == 0) - module->addWire(RTLIL::escape_id(q)); - - module->connect(module->wires_.at(RTLIL::escape_id(q)), module->wires_.at(RTLIL::escape_id(p))); + module->connect(blif_wire(q), blif_wire(p)); continue; } @@ -251,19 +299,13 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name) { char *p; RTLIL::SigSpec input_sig, output_sig; - while ((p = strtok(NULL, " \t\r\n")) != NULL) { - RTLIL::Wire *wire; - if (module->wires_.count(RTLIL::escape_id(p)) > 0) { - wire = module->wires_.at(RTLIL::escape_id(p)); - } else { - wire = module->addWire(RTLIL::escape_id(p)); - } - input_sig.append(wire); - } + while ((p = strtok(NULL, " \t\r\n")) != NULL) + input_sig.append(blif_wire(p)); output_sig = input_sig.extract(input_sig.size()-1, 1); input_sig = input_sig.extract(0, input_sig.size()-1); - if (input_sig.size() == 0) { + if (input_sig.size() == 0) + { RTLIL::State state = RTLIL::State::Sa; while (1) { if (!read_next_line(buffer, buffer_size, line_count, f)) @@ -288,9 +330,12 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name) goto error; } } + finished_parsing_constval: if (state == RTLIL::State::Sa) state = RTLIL::State::S0; + if (output_sig.as_wire()->name == "$undef") + state = RTLIL::State::Sx; module->connect(RTLIL::SigSig(output_sig, state)); goto continue_without_read; } @@ -367,7 +412,7 @@ struct BlifFrontend : public Frontend { } extra_args(f, filename, args, argidx); - parse_blif(design, *f, "\\DFF"); + parse_blif(design, *f, "\\DFF", true); } } BlifFrontend; diff --git a/frontends/blif/blifparse.h b/frontends/blif/blifparse.h index 4d7f59d6..3c01ed37 100644 --- a/frontends/blif/blifparse.h +++ b/frontends/blif/blifparse.h @@ -24,7 +24,7 @@ YOSYS_NAMESPACE_BEGIN -extern void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name); +extern void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean = false); YOSYS_NAMESPACE_END diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y index 6090fabe..cc31c864 100644 --- a/frontends/ilang/ilang_parser.y +++ b/frontends/ilang/ilang_parser.y @@ -50,6 +50,7 @@ USING_YOSYS_NAMESPACE int integer; YOSYS_NAMESPACE_PREFIX RTLIL::Const *data; YOSYS_NAMESPACE_PREFIX RTLIL::SigSpec *sigspec; + std::vector<YOSYS_NAMESPACE_PREFIX RTLIL::SigSpec> *rsigspec; } %token <string> TOK_ID TOK_VALUE TOK_STRING @@ -60,6 +61,7 @@ USING_YOSYS_NAMESPACE %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 +%type <rsigspec> sigspec_list_reversed %type <sigspec> sigspec sigspec_list %type <integer> sync_type %type <data> constant @@ -121,7 +123,7 @@ attr_stmt: autoidx_stmt: TOK_AUTOIDX TOK_INT EOL { - autoidx = std::max(autoidx, $2); + autoidx = max(autoidx, $2); }; wire_stmt: @@ -274,8 +276,8 @@ compare_list: /* empty */; case_body: - switch_stmt case_body | - assign_stmt case_body | + case_body switch_stmt | + case_body assign_stmt | /* empty */; assign_stmt: @@ -389,16 +391,20 @@ sigspec: $$ = $2; }; -sigspec_list: - sigspec_list sigspec { - $$ = new RTLIL::SigSpec; - $$->append(*$2); - $$->append(*$1); - delete $1; +sigspec_list_reversed: + sigspec_list_reversed sigspec { + $$->push_back(*$2); delete $2; } | /* empty */ { + $$ = new std::vector<RTLIL::SigSpec>; + }; + +sigspec_list: sigspec_list_reversed { $$ = new RTLIL::SigSpec; + for (auto it = $1->rbegin(); it != $1->rend(); it++) + $$->append(*it); + delete $1; }; conn_stmt: diff --git a/frontends/verific/Makefile.inc b/frontends/verific/Makefile.inc index 13f242c4..68ef9aed 100644 --- a/frontends/verific/Makefile.inc +++ b/frontends/verific/Makefile.inc @@ -8,8 +8,9 @@ EXTRA_TARGETS += share/verific share/verific: $(P) rm -rf share/verific.new $(Q) mkdir -p share/verific.new - $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs share/verific.new/vhdl_vdbs_1993 - $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2008 share/verific.new/vhdl_vdbs_2008 + $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1987/. share/verific.new/vhdl_vdbs_1987 + $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1993/. share/verific.new/vhdl_vdbs_1993 + $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2008/. share/verific.new/vhdl_vdbs_2008 $(Q) mv share/verific.new share/verific endif diff --git a/frontends/verific/build_amd64.txt b/frontends/verific/build_amd64.txt index 0a220475..d6952820 100644 --- a/frontends/verific/build_amd64.txt +++ b/frontends/verific/build_amd64.txt @@ -8,8 +8,6 @@ only have the i386 eval version of Verific: --snip-- CONFIG := clang ENABLE_TCL := 0 -ENABLE_QT4 := 0 -ENABLE_ABC := 0 ENABLE_PLUGINS := 0 ENABLE_VERIFIC := 1 CXXFLAGS += -m32 diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 793c0684..b0fdedcc 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -186,6 +186,16 @@ static bool import_netlist_instance_gates(RTLIL::Module *module, std::map<Net*, return true; } + if (inst->Type() == PRIM_XNOR) { + module->addXnorGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput())); + return true; + } + + if (inst->Type() == PRIM_BUF) { + module->addBufGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput())); + return true; + } + if (inst->Type() == PRIM_INV) { module->addNotGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput())); return true; @@ -314,6 +324,16 @@ static bool import_netlist_instance_cells(RTLIL::Module *module, std::map<Net*, return true; } + if (inst->Type() == PRIM_DLATCHRS) + { + if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd()) + module->addDlatch(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetControl()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput())); + else + module->addDlatchsr(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetControl()), net_map.at(inst->GetSet()), net_map.at(inst->GetReset()), + net_map.at(inst->GetInput()), net_map.at(inst->GetOutput())); + return true; + } + #define IN operatorInput(inst, net_map) #define IN1 operatorInput1(inst, net_map) #define IN2 operatorInput2(inst, net_map) @@ -359,6 +379,26 @@ static bool import_netlist_instance_cells(RTLIL::Module *module, std::map<Net*, return true; } + if (inst->Type() == OPER_ENABLED_DECODER) { + RTLIL::SigSpec vec; + vec.append(net_map.at(inst->GetControl())); + for (unsigned i = 1; i < inst->OutputSize(); i++) { + vec.append(RTLIL::State::S0); + } + module->addShl(RTLIL::escape_id(inst->Name()), vec, IN, OUT, false); + return true; + } + + if (inst->Type() == OPER_DECODER) { + RTLIL::SigSpec vec; + vec.append(RTLIL::State::S1); + for (unsigned i = 1; i < inst->OutputSize(); i++) { + vec.append(RTLIL::State::S0); + } + module->addShl(RTLIL::escape_id(inst->Name()), vec, IN, OUT, false); + return true; + } + if (inst->Type() == OPER_SHIFT_RIGHT) { Net *net_cin = inst->GetCin(); Net *net_a_msb = inst->GetInput1Bit(0); @@ -541,7 +581,7 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist* // log(" importing portbus %s.\n", portbus->Name()); RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(portbus->Name()), portbus->Size()); - wire->start_offset = std::min(portbus->LeftIndex(), portbus->RightIndex()); + wire->start_offset = min(portbus->LeftIndex(), portbus->RightIndex()); import_attributes(wire->attributes, portbus); if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN) @@ -580,11 +620,11 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist* int bits_in_word = number_of_bits; FOREACH_PORTREF_OF_NET(net, si, pr) { if (pr->GetInst()->Type() == OPER_READ_PORT) { - bits_in_word = std::min<int>(bits_in_word, pr->GetInst()->OutputSize()); + bits_in_word = min<int>(bits_in_word, pr->GetInst()->OutputSize()); continue; } if (pr->GetInst()->Type() == OPER_WRITE_PORT || pr->GetInst()->Type() == OPER_CLOCKED_WRITE_PORT) { - bits_in_word = std::min<int>(bits_in_word, pr->GetInst()->Input2Size()); + bits_in_word = min<int>(bits_in_word, pr->GetInst()->Input2Size()); continue; } log_error("Verific RamNet %s is connected to unsupported instance type %s (%s).\n", @@ -630,7 +670,7 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist* RTLIL::IdString wire_name = module->uniquify(RTLIL::escape_id(netbus->Name())); RTLIL::Wire *wire = module->addWire(wire_name, netbus->Size()); - wire->start_offset = std::min(netbus->LeftIndex(), netbus->RightIndex()); + wire->start_offset = min(netbus->LeftIndex(), netbus->RightIndex()); import_attributes(wire->attributes, netbus); for (int i = netbus->LeftIndex();; i += netbus->IsUp() ? +1 : -1) { @@ -666,6 +706,11 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist* continue; } + if (inst->Type() == PRIM_BUF) { + module->addBufGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput())); + continue; + } + if (inst->Type() == PRIM_X) { module->connect(net_map.at(inst->GetOutput()), RTLIL::State::Sx); continue; @@ -738,13 +783,15 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist* } if (inst->IsPrimitive()) - log_error("Unsupported Verific primitive: %s\n", inst->View()->Owner()->Name()); + log_error("Unsupported Verific primitive %s of type %s\n", inst->Name(), inst->View()->Owner()->Name()); nl_todo.insert(inst->View()); RTLIL::Cell *cell = module->addCell(RTLIL::escape_id(inst->Name()), inst->IsOperator() ? std::string("$verific$") + inst->View()->Owner()->Name() : RTLIL::escape_id(inst->View()->Owner()->Name())); + dict<IdString, vector<SigBit>> cell_port_conns; + FOREACH_PORTREF_OF_INST(inst, mi2, pr) { // log(" .%s(%s)\n", pr->GetPort()->Name(), pr->GetNet()->Name()); const char *port_name = pr->GetPort()->Name(); @@ -752,18 +799,21 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist* if (pr->GetPort()->Bus()) { port_name = pr->GetPort()->Bus()->Name(); port_offset = pr->GetPort()->Bus()->IndexOf(pr->GetPort()) - - std::min(pr->GetPort()->Bus()->LeftIndex(), pr->GetPort()->Bus()->RightIndex()); + min(pr->GetPort()->Bus()->LeftIndex(), pr->GetPort()->Bus()->RightIndex()); } - RTLIL::SigSpec conn; - if (cell->hasPort(RTLIL::escape_id(port_name))) - conn = cell->getPort(RTLIL::escape_id(port_name)); - while (GetSize(conn) <= port_offset) { - if (pr->GetPort()->GetDir() != DIR_IN) - conn.append(module->addWire(NEW_ID, port_offset - GetSize(conn))); - conn.append(RTLIL::State::Sz); + IdString port_name_id = RTLIL::escape_id(port_name); + auto &sigvec = cell_port_conns[port_name_id]; + if (GetSize(sigvec) <= port_offset) { + SigSpec zwires = module->addWire(NEW_ID, port_offset+1-GetSize(sigvec)); + for (auto bit : zwires) + sigvec.push_back(bit); } - conn.replace(port_offset, net_map.at(pr->GetNet())); - cell->setPort(RTLIL::escape_id(port_name), conn); + sigvec[port_offset] = net_map.at(pr->GetNet()); + } + + for (auto &it : cell_port_conns) { + // log(" .%s(%s)\n", log_id(it.first), log_signal(it.second)); + cell->setPort(it.first, it.second); } } } @@ -841,7 +891,7 @@ struct VerificPass : public Pass { } if (args.size() > 1 && args[1] == "-vhdl87") { - vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str()); + vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1987").c_str()); for (size_t argidx = 2; argidx < args.size(); argidx++) if (!vhdl_file::Analyze(args[argidx].c_str(), "work", vhdl_file::VHDL_87)) log_cmd_error("Reading `%s' in VHDL_87 mode failed.\n", args[argidx].c_str()); @@ -918,10 +968,12 @@ struct VerificPass : public Pass { for (; argidx < args.size(); argidx++) { if (veri_file::GetModule(args[argidx].c_str())) { + log("Running veri_file::Elaborate(\"%s\").\n", args[argidx].c_str()); if (!veri_file::Elaborate(args[argidx].c_str())) log_cmd_error("Elaboration of top module `%s' failed.\n", args[argidx].c_str()); nl_todo.insert(Netlist::PresentDesign()); } else { + log("Running vhdl_file::Elaborate(\"%s\").\n", args[argidx].c_str()); if (!vhdl_file::Elaborate(args[argidx].c_str())) log_cmd_error("Elaboration of top module `%s' failed.\n", args[argidx].c_str()); nl_todo.insert(Netlist::PresentDesign()); diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index e0446e08..863fee59 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -759,7 +759,7 @@ assign_expr_list: assign_expr | assign_expr_list ',' assign_expr; assign_expr: - expr '=' expr { + lvalue '=' expr { ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, $1, $3)); }; @@ -963,7 +963,7 @@ simple_behavioral_stmt: // this production creates the obligatory if-else shift/reduce conflict behavioral_stmt: - defattr | assert | wire_decl | + defattr | assert | wire_decl | param_decl | localparam_decl | non_opt_delay behavioral_stmt | simple_behavioral_stmt ';' | ';' | hierarchical_id attr { diff --git a/kernel/calc.cc b/kernel/calc.cc index 32c06c18..a24fa2ab 100644 --- a/kernel/calc.cc +++ b/kernel/calc.cc @@ -154,7 +154,7 @@ static RTLIL::Const logic_wrapper(RTLIL::State(*logic_func)(RTLIL::State, RTLIL: RTLIL::Const arg1, RTLIL::Const arg2, bool signed1, bool signed2, int result_len = -1) { if (result_len < 0) - result_len = std::max(arg1.bits.size(), arg2.bits.size()); + result_len = max(arg1.bits.size(), arg2.bits.size()); extend_u0(arg1, result_len, signed1); extend_u0(arg2, result_len, signed2); @@ -310,7 +310,7 @@ RTLIL::Const RTLIL::const_shl(const RTLIL::Const &arg1, const RTLIL::Const &arg2 RTLIL::Const RTLIL::const_shr(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool, int result_len) { RTLIL::Const arg1_ext = arg1; - extend_u0(arg1_ext, std::max(result_len, GetSize(arg1)), signed1); + extend_u0(arg1_ext, max(result_len, GetSize(arg1)), signed1); return const_shift_worker(arg1_ext, arg2, false, +1, result_len); } @@ -389,7 +389,7 @@ RTLIL::Const RTLIL::const_eq(const RTLIL::Const &arg1, const RTLIL::Const &arg2, RTLIL::Const arg2_ext = arg2; RTLIL::Const result(RTLIL::State::S0, result_len); - int width = std::max(arg1_ext.bits.size(), arg2_ext.bits.size()); + int width = max(arg1_ext.bits.size(), arg2_ext.bits.size()); extend_u0(arg1_ext, width, signed1 && signed2); extend_u0(arg2_ext, width, signed1 && signed2); @@ -423,7 +423,7 @@ RTLIL::Const RTLIL::const_eqx(const RTLIL::Const &arg1, const RTLIL::Const &arg2 RTLIL::Const arg2_ext = arg2; RTLIL::Const result(RTLIL::State::S0, result_len); - int width = std::max(arg1_ext.bits.size(), arg2_ext.bits.size()); + int width = max(arg1_ext.bits.size(), arg2_ext.bits.size()); extend_u0(arg1_ext, width, signed1 && signed2); extend_u0(arg2_ext, width, signed1 && signed2); @@ -472,21 +472,21 @@ RTLIL::Const RTLIL::const_add(const RTLIL::Const &arg1, const RTLIL::Const &arg2 { int undef_bit_pos = -1; BigInteger y = const2big(arg1, signed1, undef_bit_pos) + const2big(arg2, signed2, undef_bit_pos); - return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos); + return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos); } RTLIL::Const RTLIL::const_sub(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; BigInteger y = const2big(arg1, signed1, undef_bit_pos) - const2big(arg2, signed2, undef_bit_pos); - return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos); + return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), undef_bit_pos); } RTLIL::Const RTLIL::const_mul(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; BigInteger y = const2big(arg1, signed1, undef_bit_pos) * const2big(arg2, signed2, undef_bit_pos); - return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0)); + return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_div(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) @@ -499,7 +499,7 @@ RTLIL::Const RTLIL::const_div(const RTLIL::Const &arg1, const RTLIL::Const &arg2 bool result_neg = (a.getSign() == BigInteger::negative) != (b.getSign() == BigInteger::negative); a = a.getSign() == BigInteger::negative ? -a : a; b = b.getSign() == BigInteger::negative ? -b : b; - return big2const(result_neg ? -(a / b) : (a / b), result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0)); + return big2const(result_neg ? -(a / b) : (a / b), result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_mod(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) @@ -512,7 +512,7 @@ RTLIL::Const RTLIL::const_mod(const RTLIL::Const &arg1, const RTLIL::Const &arg2 bool result_neg = a.getSign() == BigInteger::negative; a = a.getSign() == BigInteger::negative ? -a : a; b = b.getSign() == BigInteger::negative ? -b : b; - return big2const(result_neg ? -(a % b) : (a % b), result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0)); + return big2const(result_neg ? -(a % b) : (a % b), result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) @@ -563,7 +563,7 @@ RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2 y *= -1; } - return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0)); + return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_pos(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len) diff --git a/kernel/cellaigs.cc b/kernel/cellaigs.cc index be2e7bbb..41f81355 100644 --- a/kernel/cellaigs.cc +++ b/kernel/cellaigs.cc @@ -392,7 +392,7 @@ Aig::Aig(Cell *cell) if (cell->type.in("$eq", "$ne")) { - int width = std::max(GetSize(cell->getPort("\\A")), GetSize(cell->getPort("\\B"))); + int width = max(GetSize(cell->getPort("\\A")), GetSize(cell->getPort("\\B"))); vector<int> A = mk.inport_vec("\\A", width); vector<int> B = mk.inport_vec("\\B", width); int Y = mk.bool_node(false); diff --git a/kernel/driver.cc b/kernel/driver.cc index 95835951..02e332f9 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -404,7 +404,7 @@ int main(int argc, char **argv) log("%s\n", yosys_version_str); int64_t total_ns = 0; - std::set<std::tuple<int64_t, int, std::string>> timedat; + std::set<tuple<int64_t, int, std::string>> timedat; for (auto &it : pass_register) if (it.second->call_counter) { diff --git a/kernel/hashlib.h b/kernel/hashlib.h index 3cc95b6e..280b1693 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -74,7 +74,7 @@ template<> struct hash_ops<int32_t> : hash_int_ops template<> struct hash_ops<int64_t> : hash_int_ops { static inline unsigned int hash(int64_t a) { - return mkhash(a, a >> 32); + return mkhash((unsigned int)(a), (unsigned int)(a >> 32)); } }; @@ -95,9 +95,7 @@ template<typename P, typename Q> struct hash_ops<std::pair<P, Q>> { return a == b; } static inline unsigned int hash(std::pair<P, Q> a) { - hash_ops<P> p_ops; - hash_ops<Q> q_ops; - return mkhash(p_ops.hash(a.first), q_ops.hash(a.second)); + return mkhash(hash_ops<P>::hash(a.first), hash_ops<Q>::hash(a.second)); } }; @@ -111,8 +109,8 @@ template<typename... T> struct hash_ops<std::tuple<T...>> { } template<size_t I = 0> static inline typename std::enable_if<I != sizeof...(T), unsigned int>::type hash(std::tuple<T...> a) { - hash_ops<typename std::tuple_element<I, std::tuple<T...>>::type> element_ops; - return mkhash(hash<I+1>(a), element_ops.hash(std::get<I>(a))); + typedef hash_ops<typename std::tuple_element<I, std::tuple<T...>>::type> element_ops_t; + return mkhash(hash<I+1>(a), element_ops_t::hash(std::get<I>(a))); } }; @@ -162,6 +160,11 @@ struct hash_obj_ops { } }; +template<typename T> +inline unsigned int mkhash(const T &v) { + return hash_ops<T>().hash(v); +} + inline int hashtable_size(int min_size) { static std::vector<int> zero_and_some_primes = { @@ -190,6 +193,7 @@ inline int hashtable_size(int min_size) template<typename K, typename T, typename OPS = hash_ops<K>> class dict; template<typename K, int offset = 0, typename OPS = hash_ops<K>> class idict; template<typename K, typename OPS = hash_ops<K>> class pool; +template<typename K, typename OPS = hash_ops<K>> class mfp; template<typename K, typename T, typename OPS> class dict @@ -227,7 +231,7 @@ class dict void do_rehash() { hashtable.clear(); - hashtable.resize(hashtable_size(entries.size() * hashtable_size_factor), -1); + hashtable.resize(hashtable_size(entries.capacity() * hashtable_size_factor), -1); for (int i = 0; i < int(entries.size()); i++) { do_assert(-1 <= entries[i].next && entries[i].next < int(entries.size())); @@ -500,6 +504,15 @@ public: return entries[i].udata.second; } + T at(const K &key, const T &defval) const + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + if (i < 0) + return defval; + return entries[i].udata.second; + } + T& operator[](const K &key) { int hash = do_hash(key); @@ -537,6 +550,7 @@ public: return !operator==(other); } + void reserve(size_t n) { entries.reserve(n); } size_t size() const { return entries.size(); } bool empty() const { return entries.empty(); } void clear() { hashtable.clear(); entries.clear(); } @@ -586,7 +600,7 @@ protected: void do_rehash() { hashtable.clear(); - hashtable.resize(hashtable_size(entries.size() * hashtable_size_factor), -1); + hashtable.resize(hashtable_size(entries.capacity() * hashtable_size_factor), -1); for (int i = 0; i < int(entries.size()); i++) { do_assert(-1 <= entries[i].next && entries[i].next < int(entries.size())); @@ -853,6 +867,7 @@ public: return !operator==(other); } + void reserve(size_t n) { entries.reserve(n); } size_t size() const { return entries.size(); } bool empty() const { return entries.empty(); } void clear() { hashtable.clear(); entries.clear(); } @@ -890,6 +905,15 @@ public: return i + offset; } + int at(const K &key, int defval) const + { + int hash = database.do_hash(key); + int i = database.do_lookup(key, hash); + if (i < 0) + return defval; + return i + offset; + } + int count(const K &key) const { int hash = database.do_hash(key); @@ -909,6 +933,115 @@ public: return database.entries.at(index - offset).udata; } + void swap(idict &other) + { + database.swap(other.database); + } + + void reserve(size_t n) { database.reserve(n); } + size_t size() const { return database.size(); } + bool empty() const { return database.empty(); } + void clear() { database.clear(); } + + const_iterator begin() const { return database.begin(); } + const_iterator end() const { return database.end(); } +}; + +template<typename K, typename OPS> +class mfp +{ + mutable idict<K, 0, OPS> database; + mutable std::vector<int> parents; + +public: + typedef typename idict<K, 0, OPS>::const_iterator const_iterator; + + int operator()(const K &key) const + { + int i = database(key); + parents.resize(database.size(), -1); + return i; + } + + const K &operator[](int index) const + { + return database[index]; + } + + int ifind(int i) const + { + int p = i, k = i; + + while (parents[p] != -1) + p = parents[p]; + + while (k != p) { + int next_k = parents[k]; + parents[k] = p; + k = next_k; + } + + return p; + } + + void imerge(int i, int j) + { + i = ifind(i); + j = ifind(j); + + if (i != j) + parents[i] = j; + } + + void ipromote(int i) + { + int k = i; + + while (k != -1) { + int next_k = parents[k]; + parents[k] = i; + k = next_k; + } + + parents[i] = -1; + } + + int lookup(const K &a) const + { + return ifind((*this)(a)); + } + + const K &find(const K &a) const + { + int i = database.at(a, -1); + if (i < 0) + return a; + return (*this)[ifind(i)]; + } + + void merge(const K &a, const K &b) + { + imerge((*this)(a), (*this)(b)); + } + + void promote(const K &a) + { + int i = database.at(a, -1); + if (i >= 0) + ipromote(i); + } + + void swap(mfp &other) + { + database.swap(other.database); + parents.swap(other.parents); + } + + void reserve(size_t n) { database.reserve(n); } + size_t size() const { return database.size(); } + bool empty() const { return database.empty(); } + void clear() { database.clear(); parents.clear(); } + const_iterator begin() const { return database.begin(); } const_iterator end() const { return database.end(); } }; diff --git a/kernel/macc.h b/kernel/macc.h index 7efd0228..286ce567 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -158,8 +158,8 @@ struct Macc int max_size = 0, num_bits = 0; for (auto &port : ports) { - max_size = std::max(max_size, GetSize(port.in_a)); - max_size = std::max(max_size, GetSize(port.in_b)); + max_size = max(max_size, GetSize(port.in_a)); + max_size = max(max_size, GetSize(port.in_b)); } while (max_size) diff --git a/kernel/modtools.h b/kernel/modtools.h index 44c1bde1..ffcb48d4 100644 --- a/kernel/modtools.h +++ b/kernel/modtools.h @@ -180,8 +180,8 @@ struct ModIndex : public RTLIL::Monitor { RTLIL::SigBit lhs = sigmap(sigsig.first[i]); RTLIL::SigBit rhs = sigmap(sigsig.second[i]); - bool has_lhs = database.count(lhs); - bool has_rhs = database.count(rhs); + bool has_lhs = database.count(lhs) != 0; + bool has_rhs = database.count(rhs) != 0; if (!has_lhs && !has_rhs) { sigmap.add(lhs, rhs); @@ -226,7 +226,7 @@ struct ModIndex : public RTLIL::Monitor auto_reload_module = true; } - ModIndex(RTLIL::Module *_m) : module(_m) + ModIndex(RTLIL::Module *_m) : sigmap(_m), module(_m) { auto_reload_counter = 0; auto_reload_module = true; @@ -274,6 +274,27 @@ struct ModIndex : public RTLIL::Monitor return empty_result_set; return info->ports; } + + void dump_db() + { + log("--- ModIndex Dump ---\n"); + + if (auto_reload_module) { + log("AUTO-RELOAD\n"); + reload_module(); + } + + for (auto &it : database) { + log("BIT %s:\n", log_signal(it.first)); + if (it.second.is_input) + log(" PRIMARY INPUT\n"); + if (it.second.is_output) + log(" PRIMARY OUTPUT\n"); + for (auto &port : it.second.ports) + log(" PORT: %s.%s[%d] (%s)\n", log_id(port.cell), + log_id(port.port), port.offset, log_id(port.cell->type)); + } + } }; struct ModWalker diff --git a/kernel/register.cc b/kernel/register.cc index 179d064f..49a67324 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -209,7 +209,7 @@ void Pass::call(RTLIL::Design *design, std::string command) void Pass::call(RTLIL::Design *design, std::vector<std::string> args) { - if (args.size() == 0 || args[0][0] == '#') + if (args.size() == 0 || args[0][0] == '#' || args[0][0] == ':') return; if (echo_mode) { @@ -534,14 +534,28 @@ void Backend::backend_call(RTLIL::Design *design, std::ostream *f, std::string f design->check(); } +static struct CellHelpMessages { + dict<string, string> cell_help, cell_code; + CellHelpMessages() { +#include "techlibs/common/simlib_help.inc" +#include "techlibs/common/simcells_help.inc" + cell_help.sort(); + cell_code.sort(); + } +} cell_help_messages; + struct HelpPass : public Pass { HelpPass() : Pass("help", "display help messages") { } virtual void help() { log("\n"); - log(" help ............. list all commands\n"); - log(" help <command> ... print help message for given command\n"); - log(" help -all ........ print complete command reference\n"); + log(" help ................ list all commands\n"); + log(" help <command> ...... print help message for given command\n"); + log(" help -all ........... print complete command reference\n"); + log("\n"); + log(" help -cells .......... list all cell types\n"); + log(" help <celltype> ..... print help message for given cell type\n"); + log(" help <celltype>+ .... print verilog code for given cell type\n"); log("\n"); } void escape_tex(std::string &tex) @@ -609,6 +623,7 @@ struct HelpPass : public Pass { log(" %-20s %s\n", it.first.c_str(), it.second->short_help.c_str()); log("\n"); log("Type 'help <command>' for more information on a command.\n"); + log("Type 'help -cells' for a list of all cell types.\n"); log("\n"); return; } @@ -624,6 +639,18 @@ struct HelpPass : public Pass { it.second->help(); } } + else if (args[1] == "-cells") { + log("\n"); + for (auto &it : cell_help_messages.cell_help) { + string line = split_tokens(it.second, "\n").at(0); + string cell_name = next_token(line); + log(" %-15s %s\n", cell_name.c_str(), line.c_str()); + } + log("\n"); + log("Type 'help <cell_type>' for more information on a cell type.\n"); + log("\n"); + return; + } // this option is undocumented as it is for internal use only else if (args[1] == "-write-tex-command-reference-manual") { FILE *f = fopen("command-reference-manual.tex", "wt"); @@ -649,10 +676,20 @@ struct HelpPass : public Pass { } fclose(f); } - else if (pass_register.count(args[1]) == 0) - log("No such command: %s\n", args[1].c_str()); - else + else if (pass_register.count(args[1])) { pass_register.at(args[1])->help(); + } + else if (cell_help_messages.cell_help.count(args[1])) { + log("%s", cell_help_messages.cell_help.at(args[1]).c_str()); + log("Run 'help %s+' to display the Verilog model for this cell type.\n", args[1].c_str()); + log("\n"); + } + else if (cell_help_messages.cell_code.count(args[1])) { + log("\n"); + log("%s", cell_help_messages.cell_code.at(args[1]).c_str()); + } + else + log("No such command or cell type: %s\n", args[1].c_str()); return; } diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 7090fe91..a706491e 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -981,11 +981,11 @@ namespace { param("\\SIZE"); param("\\OFFSET"); param("\\INIT"); - param_bits("\\RD_CLK_ENABLE", std::max(1, param("\\RD_PORTS"))); - param_bits("\\RD_CLK_POLARITY", std::max(1, param("\\RD_PORTS"))); - param_bits("\\RD_TRANSPARENT", std::max(1, param("\\RD_PORTS"))); - param_bits("\\WR_CLK_ENABLE", std::max(1, param("\\WR_PORTS"))); - param_bits("\\WR_CLK_POLARITY", std::max(1, param("\\WR_PORTS"))); + param_bits("\\RD_CLK_ENABLE", max(1, param("\\RD_PORTS"))); + param_bits("\\RD_CLK_POLARITY", max(1, param("\\RD_PORTS"))); + param_bits("\\RD_TRANSPARENT", max(1, param("\\RD_PORTS"))); + param_bits("\\WR_CLK_ENABLE", max(1, param("\\WR_PORTS"))); + param_bits("\\WR_CLK_POLARITY", max(1, param("\\WR_PORTS"))); port("\\RD_CLK", param("\\RD_PORTS")); port("\\RD_EN", param("\\RD_PORTS")); port("\\RD_ADDR", param("\\RD_PORTS") * param("\\ABITS")); @@ -1448,6 +1448,19 @@ void RTLIL::Module::connect(const RTLIL::SigSig &conn) for (auto mon : design->monitors) mon->notify_connect(this, conn); + // ignore all attempts to assign constants to other constants + if (conn.first.has_const()) { + RTLIL::SigSig new_conn; + for (int i = 0; i < GetSize(conn.first); i++) + if (conn.first[i].wire) { + new_conn.first.append(conn.first[i]); + new_conn.second.append(conn.second[i]); + } + if (GetSize(new_conn.first)) + connect(new_conn); + return; + } + if (yosys_xtrace) { log("#X# Connect (SigSig) in %s: %s = %s (%d bits)\n", log_id(this), log_signal(conn.first), log_signal(conn.second), GetSize(conn.first)); log_backtrace("-X- ", yosys_xtrace-1); @@ -1588,10 +1601,10 @@ DEF_METHOD(LogicNot, 1, "$logic_not") add ## _func(name, sig_a, sig_b, sig_y, is_signed); \ return sig_y; \ } -DEF_METHOD(And, std::max(sig_a.size(), sig_b.size()), "$and") -DEF_METHOD(Or, std::max(sig_a.size(), sig_b.size()), "$or") -DEF_METHOD(Xor, std::max(sig_a.size(), sig_b.size()), "$xor") -DEF_METHOD(Xnor, std::max(sig_a.size(), sig_b.size()), "$xnor") +DEF_METHOD(And, max(sig_a.size(), sig_b.size()), "$and") +DEF_METHOD(Or, max(sig_a.size(), sig_b.size()), "$or") +DEF_METHOD(Xor, max(sig_a.size(), sig_b.size()), "$xor") +DEF_METHOD(Xnor, max(sig_a.size(), sig_b.size()), "$xnor") DEF_METHOD(Shl, sig_a.size(), "$shl") DEF_METHOD(Shr, sig_a.size(), "$shr") DEF_METHOD(Sshl, sig_a.size(), "$sshl") @@ -1606,11 +1619,11 @@ DEF_METHOD(Eqx, 1, "$eqx") DEF_METHOD(Nex, 1, "$nex") DEF_METHOD(Ge, 1, "$ge") DEF_METHOD(Gt, 1, "$gt") -DEF_METHOD(Add, std::max(sig_a.size(), sig_b.size()), "$add") -DEF_METHOD(Sub, std::max(sig_a.size(), sig_b.size()), "$sub") -DEF_METHOD(Mul, std::max(sig_a.size(), sig_b.size()), "$mul") -DEF_METHOD(Div, std::max(sig_a.size(), sig_b.size()), "$div") -DEF_METHOD(Mod, std::max(sig_a.size(), sig_b.size()), "$mod") +DEF_METHOD(Add, max(sig_a.size(), sig_b.size()), "$add") +DEF_METHOD(Sub, max(sig_a.size(), sig_b.size()), "$sub") +DEF_METHOD(Mul, max(sig_a.size(), sig_b.size()), "$mul") +DEF_METHOD(Div, max(sig_a.size(), sig_b.size()), "$div") +DEF_METHOD(Mod, max(sig_a.size(), sig_b.size()), "$mod") DEF_METHOD(LogicAnd, 1, "$logic_and") DEF_METHOD(LogicOr, 1, "$logic_or") #undef DEF_METHOD @@ -1689,6 +1702,7 @@ DEF_METHOD(Pmux, "$pmux", 1) add ## _func(name, sig1, sig2, sig3, sig4, sig5); \ return sig5; \ } +DEF_METHOD_2(BufGate, "$_BUF_", A, Y) DEF_METHOD_2(NotGate, "$_NOT_", A, Y) DEF_METHOD_3(AndGate, "$_AND_", A, B, Y) DEF_METHOD_3(NandGate, "$_NAND_", A, B, Y) @@ -2560,8 +2574,18 @@ void RTLIL::SigSpec::sort() void RTLIL::SigSpec::sort_and_unify() { + unpack(); cover("kernel.rtlil.sigspec.sort_and_unify"); - *this = this->to_sigbit_set(); + + // A copy of the bits vector is used to prevent duplicating the logic from + // SigSpec::SigSpec(std::vector<SigBit>). This incurrs an extra copy but + // that isn't showing up as significant in profiles. + std::vector<SigBit> unique_bits = bits_; + std::sort(unique_bits.begin(), unique_bits.end()); + auto last = std::unique(unique_bits.begin(), unique_bits.end()); + unique_bits.erase(last, unique_bits.end()); + + *this = unique_bits; } void RTLIL::SigSpec::replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with) @@ -2571,18 +2595,26 @@ void RTLIL::SigSpec::replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec void RTLIL::SigSpec::replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with, RTLIL::SigSpec *other) const { + log_assert(other != NULL); + log_assert(width_ == other->width_); log_assert(pattern.width_ == with.width_); pattern.unpack(); with.unpack(); + unpack(); + other->unpack(); - dict<RTLIL::SigBit, RTLIL::SigBit> rules; - - for (int i = 0; i < GetSize(pattern.bits_); i++) - if (pattern.bits_[i].wire != NULL) - rules[pattern.bits_[i]] = with.bits_[i]; + for (int i = 0; i < GetSize(pattern.bits_); i++) { + if (pattern.bits_[i].wire != NULL) { + for (int j = 0; j < GetSize(bits_); j++) { + if (bits_[j] == pattern.bits_[i]) { + other->bits_[j] = with.bits_[i]; + } + } + } + } - replace(rules, other); + other->check(); } void RTLIL::SigSpec::replace(const dict<RTLIL::SigBit, RTLIL::SigBit> &rules) @@ -2646,8 +2678,35 @@ void RTLIL::SigSpec::remove(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other void RTLIL::SigSpec::remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other) { - pool<RTLIL::SigBit> pattern_bits = pattern.to_sigbit_pool(); - remove2(pattern_bits, other); + if (other) + cover("kernel.rtlil.sigspec.remove_other"); + else + cover("kernel.rtlil.sigspec.remove"); + + unpack(); + if (other != NULL) { + log_assert(width_ == other->width_); + other->unpack(); + } + + for (int i = GetSize(bits_) - 1; i >= 0; i--) { + if (bits_[i].wire == NULL) continue; + + for (auto &pattern_chunk : pattern.chunks()) { + if (bits_[i].wire == pattern_chunk.wire && + bits_[i].offset >= pattern_chunk.offset && + bits_[i].offset < pattern_chunk.offset + pattern_chunk.width) { + bits_.erase(bits_.begin() + i); + width_--; + if (other != NULL) { + other->bits_.erase(other->bits_.begin() + i); + other->width_--; + } + } + } + } + + check(); } void RTLIL::SigSpec::remove(const pool<RTLIL::SigBit> &pattern) @@ -2675,31 +2734,43 @@ void RTLIL::SigSpec::remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec other->unpack(); } - std::vector<RTLIL::SigBit> new_bits, new_other_bits; - - new_bits.resize(GetSize(bits_)); - if (other != NULL) - new_other_bits.resize(GetSize(bits_)); - - int k = 0; - for (int i = 0; i < GetSize(bits_); i++) { - if (bits_[i].wire != NULL && pattern.count(bits_[i])) - continue; - if (other != NULL) - new_other_bits[k] = other->bits_[i]; - new_bits[k++] = bits_[i]; + for (int i = GetSize(bits_) - 1; i >= 0; i--) { + if (bits_[i].wire != NULL && pattern.count(bits_[i])) { + bits_.erase(bits_.begin() + i); + width_--; + if (other != NULL) { + other->bits_.erase(other->bits_.begin() + i); + other->width_--; + } + } } - new_bits.resize(k); - if (other != NULL) - new_other_bits.resize(k); + check(); +} + +void RTLIL::SigSpec::remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other) +{ + if (other) + cover("kernel.rtlil.sigspec.remove_other"); + else + cover("kernel.rtlil.sigspec.remove"); - bits_.swap(new_bits); - width_ = GetSize(bits_); + unpack(); if (other != NULL) { - other->bits_.swap(new_other_bits); - other->width_ = GetSize(other->bits_); + log_assert(width_ == other->width_); + other->unpack(); + } + + for (int i = GetSize(bits_) - 1; i >= 0; i--) { + if (bits_[i].wire != NULL && pattern.count(bits_[i])) { + bits_.erase(bits_.begin() + i); + width_--; + if (other != NULL) { + other->bits_.erase(other->bits_.begin() + i); + other->width_--; + } + } } check(); @@ -2707,8 +2778,37 @@ void RTLIL::SigSpec::remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec RTLIL::SigSpec RTLIL::SigSpec::extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other) const { - pool<RTLIL::SigBit> pattern_bits = pattern.to_sigbit_pool(); - return extract(pattern_bits, other); + if (other) + cover("kernel.rtlil.sigspec.extract_other"); + else + cover("kernel.rtlil.sigspec.extract"); + + log_assert(other == NULL || width_ == other->width_); + + RTLIL::SigSpec ret; + std::vector<RTLIL::SigBit> bits_match = to_sigbit_vector(); + + for (auto& pattern_chunk : pattern.chunks()) { + if (other) { + std::vector<RTLIL::SigBit> bits_other = other->to_sigbit_vector(); + for (int i = 0; i < width_; i++) + if (bits_match[i].wire && + bits_match[i].wire == pattern_chunk.wire && + bits_match[i].offset >= pattern_chunk.offset && + bits_match[i].offset < pattern_chunk.offset + pattern_chunk.width) + ret.append_bit(bits_other[i]); + } else { + for (int i = 0; i < width_; i++) + if (bits_match[i].wire && + bits_match[i].wire == pattern_chunk.wire && + bits_match[i].offset >= pattern_chunk.offset && + bits_match[i].offset < pattern_chunk.offset + pattern_chunk.width) + ret.append_bit(bits_match[i]); + } + } + + ret.check(); + return ret; } RTLIL::SigSpec RTLIL::SigSpec::extract(const pool<RTLIL::SigBit> &pattern, const RTLIL::SigSpec *other) const @@ -3184,6 +3284,17 @@ RTLIL::SigChunk RTLIL::SigSpec::as_chunk() const return chunks_[0]; } +RTLIL::SigBit RTLIL::SigSpec::as_bit() const +{ + cover("kernel.rtlil.sigspec.as_bit"); + + log_assert(width_ == 1); + if (packed()) + return RTLIL::SigBit(*chunks_.begin()); + else + return bits_[0]; +} + bool RTLIL::SigSpec::match(std::string pattern) const { cover("kernel.rtlil.sigspec.match"); @@ -3271,18 +3382,6 @@ dict<RTLIL::SigBit, RTLIL::SigBit> RTLIL::SigSpec::to_sigbit_dict(const RTLIL::S return new_map; } -RTLIL::SigBit RTLIL::SigSpec::to_single_sigbit() const -{ - cover("kernel.rtlil.sigspec.to_single_sigbit"); - - pack(); - log_assert(width_ == 1); - for (auto &c : chunks_) - if (c.width) - return RTLIL::SigBit(c); - log_abort(); -} - static void sigspec_parse_split(std::vector<std::string> &tokens, const std::string &text, char sep) { size_t start = 0, end = 0; diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 43ef5806..940e36ab 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -192,12 +192,12 @@ namespace RTLIL return std::string(global_id_storage_.at(index_)); } - bool operator<(IdString rhs) const { + bool operator<(const IdString &rhs) const { return index_ < rhs.index_; } - bool operator==(IdString rhs) const { return index_ == rhs.index_; } - bool operator!=(IdString rhs) const { return index_ != rhs.index_; } + bool operator==(const IdString &rhs) const { return index_ == rhs.index_; } + bool operator!=(const IdString &rhs) const { return index_ != rhs.index_; } // The methods below are just convenience functions for better compatibility with std::string. @@ -460,7 +460,7 @@ struct RTLIL::Const Const(std::string str); Const(int val, int width = 32); Const(RTLIL::State bit, int width = 1); - Const(const std::vector<RTLIL::State> &bits) : bits(bits) { flags = CONST_FLAG_NONE; }; + Const(const std::vector<RTLIL::State> &bits) : bits(bits) { flags = CONST_FLAG_NONE; } Const(const std::vector<bool> &bits); bool operator <(const RTLIL::Const &other) const; @@ -476,7 +476,7 @@ struct RTLIL::Const inline int size() const { return bits.size(); } inline RTLIL::State &operator[](int index) { return bits.at(index); } - inline const RTLIL::State &operator[](int index) const { return bits.at(index); }; + inline const RTLIL::State &operator[](int index) const { return bits.at(index); } inline RTLIL::Const extract(int offset, int len = 1, RTLIL::State padding = RTLIL::State::S0) const { RTLIL::Const ret; @@ -669,6 +669,7 @@ public: void remove(const pool<RTLIL::SigBit> &pattern); void remove(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other) const; void remove2(const pool<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other); + void remove2(const std::set<RTLIL::SigBit> &pattern, RTLIL::SigSpec *other); void remove(int offset, int length = 1); void remove_const(); @@ -690,6 +691,7 @@ public: bool is_wire() const; bool is_chunk() const; + inline bool is_bit() const { return width_ == 1; } bool is_fully_const() const; bool is_fully_zero() const; @@ -704,6 +706,7 @@ public: RTLIL::Const as_const() const; RTLIL::Wire *as_wire() const; RTLIL::SigChunk as_chunk() const; + RTLIL::SigBit as_bit() const; bool match(std::string pattern) const; @@ -712,7 +715,6 @@ public: std::vector<RTLIL::SigBit> to_sigbit_vector() const; std::map<RTLIL::SigBit, RTLIL::SigBit> to_sigbit_map(const RTLIL::SigSpec &other) const; dict<RTLIL::SigBit, RTLIL::SigBit> to_sigbit_dict(const RTLIL::SigSpec &other) const; - RTLIL::SigBit to_single_sigbit() const; static bool parse(RTLIL::SigSpec &sig, RTLIL::Module *module, std::string str); static bool parse_sel(RTLIL::SigSpec &sig, RTLIL::Design *design, RTLIL::Module *module, std::string str); @@ -1014,6 +1016,7 @@ public: RTLIL::Cell* addDlatchsr (RTLIL::IdString name, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity = true, bool set_polarity = true, bool clr_polarity = true); + RTLIL::Cell* addBufGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_y); RTLIL::Cell* addNotGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_y); RTLIL::Cell* addAndGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y); RTLIL::Cell* addNandGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y); @@ -1085,6 +1088,7 @@ public: RTLIL::SigSpec Mux (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s); RTLIL::SigSpec Pmux (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s); + RTLIL::SigBit BufGate (RTLIL::IdString name, RTLIL::SigBit sig_a); RTLIL::SigBit NotGate (RTLIL::IdString name, RTLIL::SigBit sig_a); RTLIL::SigBit AndGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b); RTLIL::SigBit NandGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b); diff --git a/kernel/satgen.h b/kernel/satgen.h index 7b099444..d44d61f1 100644 --- a/kernel/satgen.h +++ b/kernel/satgen.h @@ -980,7 +980,7 @@ struct SatGen div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), ez->CONST_FALSE); } } else { - int copy_a_bits = std::min(cell->getPort("\\A").size(), cell->getPort("\\B").size()); + int copy_a_bits = min(cell->getPort("\\A").size(), cell->getPort("\\B").size()); div_zero_result.insert(div_zero_result.end(), a.begin(), a.begin() + copy_a_bits); if (cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool()) div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), div_zero_result.back()); diff --git a/kernel/sigtools.h b/kernel/sigtools.h index a9419f87..4e97bb77 100644 --- a/kernel/sigtools.h +++ b/kernel/sigtools.h @@ -222,18 +222,7 @@ struct SigSet struct SigMap { - struct bitDef_t : public std::pair<RTLIL::Wire*, int> { - bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { } - bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { } - unsigned int hash() const { return first->name.hash() + second; } - }; - - struct shared_bit_data_t { - RTLIL::SigBit map_to; - std::set<bitDef_t> bits; - }; - - dict<bitDef_t, shared_bit_data_t*> bits; + mfp<SigBit> database; SigMap(RTLIL::Module *module = NULL) { @@ -241,119 +230,27 @@ struct SigMap set(module); } - SigMap(const SigMap &other) - { - copy(other); - } - - const SigMap &operator=(const SigMap &other) - { - copy(other); - return *this; - } - - void copy(const SigMap &other) - { - clear(); - for (auto &bit : other.bits) { - bits[bit.first] = new shared_bit_data_t; - bits[bit.first]->map_to = bit.second->map_to; - bits[bit.first]->bits = bit.second->bits; - } - } - void swap(SigMap &other) { - bits.swap(other.bits); - } - - ~SigMap() - { - clear(); + database.swap(other.database); } void clear() { - std::set<shared_bit_data_t*> all_bd_ptr; - for (auto &it : bits) - all_bd_ptr.insert(it.second); - for (auto bd_ptr : all_bd_ptr) - delete bd_ptr; - bits.clear(); + database.clear(); } void set(RTLIL::Module *module) { - clear(); + int bitcount = 0; for (auto &it : module->connections()) - add(it.first, it.second); - } + bitcount += it.first.size(); - // internal helper function - void register_bit(const RTLIL::SigBit &bit) - { - if (bit.wire && bits.count(bit) == 0) { - shared_bit_data_t *bd = new shared_bit_data_t; - bd->map_to = bit; - bd->bits.insert(bit); - bits[bit] = bd; - } - } + database.clear(); + database.reserve(bitcount); - // internal helper function - void unregister_bit(const RTLIL::SigBit &bit) - { - if (bit.wire && bits.count(bit) > 0) { - shared_bit_data_t *bd = bits[bit]; - bd->bits.erase(bit); - if (bd->bits.size() == 0) - delete bd; - bits.erase(bit); - } - } - - // internal helper function - void merge_bit(const RTLIL::SigBit &bit1, const RTLIL::SigBit &bit2) - { - log_assert(bit1.wire != NULL && bit2.wire != NULL); - - shared_bit_data_t *bd1 = bits[bit1]; - shared_bit_data_t *bd2 = bits[bit2]; - log_assert(bd1 != NULL && bd2 != NULL); - - if (bd1 == bd2) - return; - - if (bd1->bits.size() < bd2->bits.size()) - { - for (auto &bit : bd1->bits) - bits[bit] = bd2; - bd2->bits.insert(bd1->bits.begin(), bd1->bits.end()); - delete bd1; - } - else - { - bd1->map_to = bd2->map_to; - for (auto &bit : bd2->bits) - bits[bit] = bd1; - bd1->bits.insert(bd2->bits.begin(), bd2->bits.end()); - delete bd2; - } - } - - // internal helper function - void set_bit(const RTLIL::SigBit &bit1, const RTLIL::SigBit &bit2) - { - log_assert(bit1.wire != NULL); - log_assert(bits.count(bit1) > 0); - bits[bit1]->map_to = bit2; - } - - // internal helper function - void map_bit(RTLIL::SigBit &bit) const - { - if (bit.wire && bits.count(bit) > 0) - bit = bits.at(bit)->map_to; + for (auto &it : module->connections()) + add(it.first, it.second); } void add(RTLIL::SigSpec from, RTLIL::SigSpec to) @@ -362,45 +259,43 @@ struct SigMap for (int i = 0; i < GetSize(from); i++) { - RTLIL::SigBit &bf = from[i]; - RTLIL::SigBit &bt = to[i]; + int bfi = database.lookup(from[i]); + int bti = database.lookup(to[i]); + + const RTLIL::SigBit &bf = database[bfi]; + const RTLIL::SigBit &bt = database[bti]; - if (bf.wire == NULL) - continue; + if (bf.wire || bt.wire) + { + database.imerge(bfi, bti); - register_bit(bf); - register_bit(bt); + if (bf.wire == nullptr) + database.ipromote(bfi); - if (bt.wire != NULL) - merge_bit(bf, bt); - else - set_bit(bf, bt); + if (bt.wire == nullptr) + database.ipromote(bti); + } } } void add(RTLIL::SigSpec sig) { for (auto &bit : sig) { - register_bit(bit); - set_bit(bit, bit); + RTLIL::SigBit b = database.find(bit); + if (b.wire != nullptr) + database.promote(bit); } } - void del(RTLIL::SigSpec sig) - { - for (auto &bit : sig) - unregister_bit(bit); - } - void apply(RTLIL::SigBit &bit) const { - map_bit(bit); + bit = database.find(bit); } void apply(RTLIL::SigSpec &sig) const { for (auto &bit : sig) - map_bit(bit); + apply(bit); } RTLIL::SigBit operator()(RTLIL::SigBit bit) const @@ -417,7 +312,7 @@ struct SigMap RTLIL::SigSpec operator()(RTLIL::Wire *wire) const { - RTLIL::SigSpec sig(wire); + SigSpec sig(wire); apply(sig); return sig; } @@ -425,8 +320,9 @@ struct SigMap RTLIL::SigSpec allbits() const { RTLIL::SigSpec sig; - for (auto &it : bits) - sig.append(SigBit(it.first.first, it.first.second)); + for (auto &bit : database) + if (bit.wire != nullptr) + sig.append(bit); return sig; } }; diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 10991881..eba1aef1 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -124,6 +124,18 @@ void yosys_banner() log("\n"); } +int ceil_log2(int x) +{ + if (x <= 0) + return 0; + + for (int i = 0; i < 32; i++) + if (((x-1) >> i) == 0) + return i; + + log_abort(); +} + std::string stringf(const char *fmt, ...) { std::string string; @@ -168,7 +180,7 @@ std::string vstringf(const char *fmt, va_list ap) int readsome(std::istream &f, char *s, int n) { - int rc = f.readsome(s, n); + int rc = int(f.readsome(s, n)); // f.readsome() sometimes returns 0 on a non-empty stream.. if (rc == 0) { diff --git a/kernel/yosys.h b/kernel/yosys.h index 6aacd4d5..c8bc46b6 100644 --- a/kernel/yosys.h +++ b/kernel/yosys.h @@ -41,6 +41,7 @@ #include <map> #include <set> +#include <tuple> #include <vector> #include <string> #include <algorithm> @@ -138,8 +139,14 @@ YOSYS_NAMESPACE_BEGIN using std::vector; using std::string; +using std::tuple; using std::pair; +using std::make_tuple; +using std::make_pair; +using std::min; +using std::max; + // A primitive shared string implementation that does not // move its .c_str() when the object is copied or moved. struct shared_str { @@ -164,6 +171,7 @@ using hashlib::hash_obj_ops; using hashlib::dict; using hashlib::idict; using hashlib::pool; +using hashlib::mfp; namespace RTLIL { struct IdString; @@ -214,6 +222,7 @@ extern bool memhasher_active; inline void memhasher() { if (memhasher_active) memhasher_do(); } void yosys_banner(); +int ceil_log2(int x); 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); @@ -242,6 +251,8 @@ YOSYS_NAMESPACE_END YOSYS_NAMESPACE_BEGIN using RTLIL::State; +using RTLIL::SigChunk; +using RTLIL::SigSig; namespace hashlib { template<> struct hash_ops<RTLIL::State> : hash_ops<int> {}; diff --git a/libs/ezsat/ezsat.cc b/libs/ezsat/ezsat.cc index da36fb74..177bcd8a 100644 --- a/libs/ezsat/ezsat.cc +++ b/libs/ezsat/ezsat.cc @@ -1337,6 +1337,28 @@ void ezSAT::printInternalState(FILE *f) const fprintf(f, "--8<-- snap --8<--\n"); } +static int clog2(int x) +{ + int y = (x & (x - 1)); + y = (y | -y) >> 31; + + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + + x >>= 1; + x -= ((x >> 1) & 0x55555555); + x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); + x = (((x >> 4) + x) & 0x0f0f0f0f); + x += (x >> 8); + x += (x >> 16); + x = x & 0x0000003f; + + return x - y; +} + int ezSAT::onehot(const std::vector<int> &vec, bool max_only) { // Mixed one-hot/binary encoding as described by Claessen in Sec. 4.2 of @@ -1350,7 +1372,7 @@ int ezSAT::onehot(const std::vector<int> &vec, bool max_only) formula.push_back(expression(OpOr, vec)); // create binary vector - int num_bits = ceil(log2(vec.size())); + int num_bits = clog2(vec.size()); std::vector<int> bits; for (int k = 0; k < num_bits; k++) bits.push_back(literal()); diff --git a/manual/CHAPTER_Appnotes.tex b/manual/CHAPTER_Appnotes.tex index cbb01ed1..e0d09329 100644 --- a/manual/CHAPTER_Appnotes.tex +++ b/manual/CHAPTER_Appnotes.tex @@ -15,6 +15,7 @@ This appendix contains copies of the Yosys application notes. \begin{itemize} \item Yosys AppNote 010: Converting Verilog to BLIF \dotfill Page \pageref{app:010} \hskip2cm\null \item Yosys AppNote 011: Interactive Design Investigation \dotfill Page \pageref{app:011} \hskip2cm\null +\item Yosys AppNote 012: Converting Verilog to BTOR \dotfill Page \pageref{app:012} \hskip2cm\null \end{itemize} \eject\label{app:010} @@ -23,3 +24,6 @@ This appendix contains copies of the Yosys application notes. \eject\label{app:011} \includepdf[pages=-,pagecommand=\thispagestyle{plain}]{APPNOTE_011_Design_Investigation.pdf} +\eject\label{app:012} +\includepdf[pages=-,pagecommand=\thispagestyle{plain}]{APPNOTE_012_Verilog_to_BTOR.pdf} + diff --git a/manual/clean.sh b/manual/clean.sh index f4a2ea83..11c2e7bf 100755 --- a/manual/clean.sh +++ b/manual/clean.sh @@ -1,2 +1,2 @@ #!/bin/bash -for f in $( find . -name .gitignore ); do sed -re "s,^,find ${f%.gitignore} -name ',; s,$,' | xargs -r rm -f,;" $f; done | bash -v +for f in $( find . -name .gitignore ); do sed -re "s,^,find ${f%.gitignore} -name ',; s,$,' | xargs rm -f,;" $f; done | bash -v diff --git a/manual/command-reference-manual.tex b/manual/command-reference-manual.tex index dfef1bb0..99d4a1fa 100644 --- a/manual/command-reference-manual.tex +++ b/manual/command-reference-manual.tex @@ -79,6 +79,15 @@ library to a target architecture. the area cost doubles with each additional input bit. the delay cost is still constant for all lut widths. + -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,.. + generate netlist using luts. Use the specified costs for luts with 1, + 2, 3, .. inputs. + + -g type1,type2,... + Map the the specified list of gate types. Supported gates types are: + AND, NAND, OR, NOR, XOR, XNOR, MUX, AOI3, OAI3, AOI4, OAI4. + (The NOT gate is always added to this list automatically.) + -dff also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many clock domains are automatically partitioned in clock domains and each @@ -450,6 +459,15 @@ to the internal cell types that best match the cells found in the given liberty file. \end{lstlisting} +\section{dffsr2dff -- convert DFFSR cells to simpler FF cell types} +\label{cmd:dffsr2dff} +\begin{lstlisting}[numbers=left,frame=single] + dffsr2dff [options] [selection] + +This pass converts DFFSR cells ($dffsr, $_DFFSR_???_) and ADFF cells ($adff, +$_DFF_???_) to simpler FF cell types when any of the set/reset inputs is unused. +\end{lstlisting} + \section{dump -- print parts of the design in ilang format} \label{cmd:dump} \begin{lstlisting}[numbers=left,frame=single] @@ -485,12 +503,26 @@ Print all commands to log before executing them. Do not print all commands to log before executing them. (default) \end{lstlisting} +\section{edgetypes -- list all types of edges in selection} +\label{cmd:edgetypes} +\begin{lstlisting}[numbers=left,frame=single] + edgetypes [options] [selection] + +This command lists all unique types of 'edges' found in the selection. An 'edge' +is a 4-tuple of source and sink cell type and port name. +\end{lstlisting} + \section{equiv\_add -- add a \$equiv cell} \label{cmd:equiv_add} \begin{lstlisting}[numbers=left,frame=single] - equiv_add gold_sig gate_sig + equiv_add [-try] gold_sig gate_sig This command adds an $equiv cell for the specified signals. + + + equiv_add [-try] -cell gold_cell gate_cell + +This command adds $equiv cells for the ports of the specified cells. \end{lstlisting} \section{equiv\_induct -- proving \$equiv cells using temporal induction} @@ -546,6 +578,17 @@ a trigger output), but instead uses $equiv cells to encode the equivalence checking problem. Use 'miter -equiv' if you want to create a miter circuit. \end{lstlisting} +\section{equiv\_mark -- mark equivalence checking regions} +\label{cmd:equiv_mark} +\begin{lstlisting}[numbers=left,frame=single] + equiv_mark [options] [selection] + +This command marks the regions in an equivalence checking module. Region 0 is +the proven part of the circuit. Regions with higher numbers are connected +unproven subcricuits. The integer attribute 'equiv_region' is set on all +wires and cells. +\end{lstlisting} + \section{equiv\_miter -- extract miter from equiv circuit} \label{cmd:equiv_miter} \begin{lstlisting}[numbers=left,frame=single] @@ -566,6 +609,16 @@ This creates a miter module for further analysis of the selected $equiv cells. Create compare logic that handles undefs correctly \end{lstlisting} +\section{equiv\_purge -- purge equivalence checking module} +\label{cmd:equiv_purge} +\begin{lstlisting}[numbers=left,frame=single] + equiv_purge [options] [selection] + +This command removes the proven part of an equivalence checking module, leaving +only the unproven segments in the design. This will also remove and add module +ports as needed. +\end{lstlisting} + \section{equiv\_remove -- remove \$equiv cells} \label{cmd:equiv_remove} \begin{lstlisting}[numbers=left,frame=single] @@ -612,6 +665,36 @@ This command prints status information for all selected $equiv cells. produce an error if any unproven $equiv cell is found \end{lstlisting} +\section{equiv\_struct -- structural equivalence checking} +\label{cmd:equiv_struct} +\begin{lstlisting}[numbers=left,frame=single] + equiv_struct [options] [selection] + +This command adds additional $equiv cells based on the assumption that the +gold and gate circuit are structurally equivalent. Note that this can introduce +bad $equiv cells in cases where the netlists are not structurally equivalent, +for example when analyzing circuits with cells with commutative inputs. This +command will also de-duplicate gates. + + -fwd + by default this command performans forward sweeps until nothing can + be merged by forwards sweeps, then backward sweeps until forward + sweeps are effective again. with this option set only forward sweeps + are performed. + + -fwonly <cell_type> + add the specified cell type to the list of cell types that are only + merged in forward sweeps and never in backward sweeps. $equiv is in + this list automatically. + + -icells + by default, the internal RTL and gate cell types are ignored. add + this option to also process those cell types with this command. + + -maxiter <N> + maximum number of iterations to run before aborting +\end{lstlisting} + \section{eval -- evaluate the circuit given an input} \label{cmd:eval} \begin{lstlisting}[numbers=left,frame=single] @@ -949,9 +1032,13 @@ one-hot encoding and binary encoding is supported. \section{help -- display help messages} \label{cmd:help} \begin{lstlisting}[numbers=left,frame=single] - help ............. list all commands - help <command> ... print help message for given command - help -all ........ print complete command reference + help ................ list all commands + help <command> ...... print help message for given command + help -all ........... print complete command reference + + help -cells .......... list all cell types + help <celltype> ..... print help message for given cell type + help <celltype>+ .... print verilog code for given cell type \end{lstlisting} \section{hierarchy -- check, expand and clean up design hierarchy} @@ -1044,6 +1131,15 @@ all commands executed in an interactive session, but not the commands from executed scripts. \end{lstlisting} +\section{ice40\_ffinit -- iCE40: handle FF init values} +\label{cmd:ice40_ffinit} +\begin{lstlisting}[numbers=left,frame=single] + ice40_ffinit [options] [selection] + +Remove zero init values for FF output signals. Add inverters to implement +nonzero init values. +\end{lstlisting} + \section{ice40\_ffssr -- iCE40: merge synchronous set/reset into FF cells} \label{cmd:ice40_ffssr} \begin{lstlisting}[numbers=left,frame=single] @@ -1078,10 +1174,10 @@ can only map to very simple PAD cells. Use 'techmap' to further map the resulting cells to more sophisticated PAD cells. -inpad <celltype> <portname>[:<portname>] - Map module input ports to the given cell type with - the given port name. if a 2nd portname is given, the + Map module input ports to the given cell type with the + given output port name. if a 2nd portname is given, the signal is passed through the pad call, using the 2nd - portname as output. + portname as input. -outpad <celltype> <portname>[:<portname>] -inoutpad <celltype> <portname>[:<portname>] @@ -1149,6 +1245,14 @@ When no active module is selected, this prints a list of modules. When an active module is selected, this prints a list of objects in the module. \end{lstlisting} +\section{lut2mux -- convert \$lut to \$\_MUX\_} +\label{cmd:lut2mux} +\begin{lstlisting}[numbers=left,frame=single] + lut2mux [options] [selection] + +This pass converts $lut cells to $_MUX_ gates. +\end{lstlisting} + \section{maccmap -- mapping macc cells} \label{cmd:maccmap} \begin{lstlisting}[numbers=left,frame=single] @@ -1196,7 +1300,7 @@ rules. A block ram description looks like this: groups 2 # number of port groups ports 1 1 # number of ports in each group wrmode 1 0 # set to '1' if this groups is write ports - enable 4 0 # number of enable bits (for write ports) + enable 4 1 # number of enable bits transp 0 2 # transparent (for read ports) clocks 1 2 # clock configuration clkpol 2 2 # clock polarity configuration @@ -1391,6 +1495,22 @@ Cover trees of $_MUX_ cells with $_MUX{4,8,16}_ cells less efficient than the original circuit. \end{lstlisting} +\section{nlutmap -- map to LUTs of different sizes} +\label{cmd:nlutmap} +\begin{lstlisting}[numbers=left,frame=single] + nlutmap [options] [selection] + +This pass uses successive calls to 'abc' to map to an architecture. That +provides a small number of differently sized LUTs. + + -luts N_1,N_2,N_3,... + The number of LUTs with 1, 2, 3, ... inputs that are + available in the target architecture. + +Excess logic that does not fit into the specified LUTs is mapped back +to generic logic gates ($_AND_, etc.). +\end{lstlisting} + \section{opt -- perform simple optimizations} \label{cmd:opt} \begin{lstlisting}[numbers=left,frame=single] @@ -1554,6 +1674,49 @@ Load and list loaded plugins. This pass transforms $pmux cells to a trees of $mux cells. \end{lstlisting} +\section{prep -- generic synthesis script} +\label{cmd:prep} +\begin{lstlisting}[numbers=left,frame=single] + prep [options] + +This command runs a conservative RTL synthesis. A typical application for this +is the preparation stage of a verification flow. This command does not operate +on partly selected designs. + + -top <module> + use the specified module as top module (default='top') + + -nordff + passed to 'memory_dff'. prohibits merging of FFs into memory read ports + + -run <from_label>[:<to_label>] + only run the commands between the labels (see below). an empty + from label is synonymous to 'begin', and empty to label is + synonymous to the end of the command list. + + +The following commands are executed by this synthesis command: + + begin: + hierarchy -check [-top <top>] + + prep: + proc + opt_const + opt_clean + check + opt -keepdc + wreduce + memory_dff [-nordff] + opt_clean + memory_collect + opt -keepdc -fast + + check: + stat + check +\end{lstlisting} + \section{proc -- translate processes to netlists} \label{cmd:proc} \begin{lstlisting}[numbers=left,frame=single] @@ -1650,6 +1813,32 @@ and case statements) to trees of multiplexer cells. This pass identifies unreachable branches in decision trees and removes them. \end{lstlisting} +\section{qwp -- quadratic wirelength placer} +\label{cmd:qwp} +\begin{lstlisting}[numbers=left,frame=single] + qwp [options] [selection] + +This command runs quadratic wirelength placement on the selected modules and +annotates the cells in the design with 'qwp_position' attributes. + + -ltr + Add left-to-right constraints: constrain all inputs on the left border + outputs to the right border. + + -alpha + Add constraints for inputs/outputs to be placed in alphanumerical + order along the y-axis (top-to-bottom). + + -grid N + Number of grid divisions in x- and y-direction. (default=16) + + -dump <html_file_name> + Dump a protocol of the placement algorithm to the html file. + +Note: This implementation of a quadratic wirelength placer uses exact +dense matrix operations. It is only a toy-placer for small circuits. +\end{lstlisting} + \section{read\_blif -- read BLIF file} \label{cmd:read_blif} \begin{lstlisting}[numbers=left,frame=single] @@ -1705,8 +1894,8 @@ Verilog-2005 is supported. of SystemVerilog is supported) -formal - enable support for assert() and assume() statements - (assert support is also enabled with -sv) + enable support for assert() and assume() from SystemVerilog + replace the implicit -D SYNTHESIS with -D FORMAL -dump_ast1 dump abstract syntax tree (before simplification) @@ -1757,6 +1946,9 @@ Verilog-2005 is supported. -nopp do not run the pre-processor + -nodpi + disable DPI-C support + -lib only create empty blackbox modules. This implies -DBLACKBOX. @@ -1870,6 +2062,9 @@ and additional constraints passed as parameters. -show-inputs, -show-outputs, -show-ports add all module (input/output) ports to the list of shown signals + -show-regs, -show-public, -show-all + show all registers, show signals with 'public' names, show all signals + -ignore_div_by_zero ignore all solutions that involve a division by zero @@ -2254,6 +2449,9 @@ The following actions can be performed on the top sets on the stack: %C select cells that implement selected modules + %R[<num>] + select <num> random objects from top selection (default 1) + Example: the following command selects all wires that are connected to a 'GATE' input of a 'SWITCH' cell: @@ -2455,10 +2653,26 @@ primitives. The following internal cell types are mapped by this pass: $not, $pos, $and, $or, $xor, $xnor $reduce_and, $reduce_or, $reduce_xor, $reduce_xnor, $reduce_bool - $logic_not, $logic_and, $logic_or, $mux + $logic_not, $logic_and, $logic_or, $mux, $tribuf $sr, $dff, $dffsr, $adff, $dlatch \end{lstlisting} +\section{singleton -- create singleton modules} +\label{cmd:singleton} +\begin{lstlisting}[numbers=left,frame=single] + singleton [selection] + +By default, a module that is instantiated by several other modules is only +kept once in the design. This preserves the original modularity of the design +and reduces the overall size of the design in memory. But it prevents certain +optimizations and other operations on the design. This pass creates singleton +modules for all selected cells. The created modules are marked with the +'singleton' attribute. + +This commands only operates on modules that by themself have the 'singleton' +attribute set (the 'top' module is a singleton implicitly). +\end{lstlisting} + \section{splice -- create explicit splicing cells} \label{cmd:splice} \begin{lstlisting}[numbers=left,frame=single] @@ -2533,6 +2747,9 @@ design. selected and a module has the 'top' attribute set, this module is used default value for this option. + -liberty <liberty_file> + use cell area information from the provided liberty file + -width annotate internal cell types with their word width. e.g. $add_8 for an 8 bit wide $add cell. @@ -2541,7 +2758,7 @@ design. \section{submod -- moving part of a module to a new submodule} \label{cmd:submod} \begin{lstlisting}[numbers=left,frame=single] - submod [selection] + submod [-copy] [selection] This pass identifies all cells with the 'submod' attribute and moves them to a newly created module. The value of the attribute is used as name for the @@ -2554,11 +2771,15 @@ This pass only operates on completely selected modules with no processes or memories. - submod -name <name> [selection] + submod -name <name> [-copy] [selection] As above, but don't use the 'submod' attribute but instead use the selection. Only objects from one module might be selected. The value of the -name option is used as the value of the 'submod' attribute above. + +By default the cells are 'moved' from the source module and the source module +will use an instance of the new module after this command is finished. Call +with -copy to not modify the source module. \end{lstlisting} \section{synth -- generic synthesis script} @@ -2601,6 +2822,7 @@ The following commands are executed by this synthesis command: coarse: proc + opt_const opt_clean check opt @@ -2628,6 +2850,79 @@ The following commands are executed by this synthesis command: check \end{lstlisting} +\section{synth\_greenpak4 -- synthesis for GreenPAK4 FPGAs} +\label{cmd:synth_greenpak4} +\begin{lstlisting}[numbers=left,frame=single] + synth_greenpak4 [options] + +This command runs synthesis for GreenPAK4 FPGAs. This work is experimental. + + -top <module> + use the specified module as top module (default='top') + + -blif <file> + write the design to the specified BLIF file. writing of an output file + is omitted if this parameter is not specified. + + -edif <file> + write the design to the specified edif file. writing of an output file + is omitted if this parameter is not specified. + + -run <from_label>:<to_label> + only run the commands between the labels (see below). an empty + from label is synonymous to 'begin', and empty to label is + synonymous to the end of the command list. + + -noflatten + do not flatten design before synthesis + + -retime + run 'abc' with -dff option + + +The following commands are executed by this synthesis command: + + begin: + read_verilog -lib +/greenpak4/cells_sim.v + hierarchy -check -top <top> + + flatten: (unless -noflatten) + proc + flatten + tribuf -logic + + coarse: + synth -run coarse + + fine: + opt -fast -mux_undef -undriven -fine + memory_map + opt -undriven -fine + techmap + dfflibmap -prepare -liberty +/greenpak4/gp_dff.lib + opt -fast + abc -dff (only if -retime) + + map_luts: + nlutmap -luts 0,8,16,2 + clean + + map_cells: + techmap -map +/greenpak4/cells_map.v + clean + + check: + hierarchy -check + stat + check -noinit + + blif: + write_blif -gates -attr -param <file-name> + + edif: + write_edif <file-name> +\end{lstlisting} + \section{synth\_ice40 -- synthesis for iCE40 FPGAs} \label{cmd:synth_ice40} \begin{lstlisting}[numbers=left,frame=single] @@ -2663,6 +2958,9 @@ This command runs synthesis for iCE40 FPGAs. This work is experimental. -nobram do not use SB_RAM40_4K* cells in output netlist + -abc2 + run two passes of 'abc' for slightly improved logic density + The following commands are executed by this synthesis command: @@ -2673,6 +2971,7 @@ The following commands are executed by this synthesis command: flatten: (unless -noflatten) proc flatten + tribuf -logic coarse: synth -run coarse @@ -2690,14 +2989,18 @@ The following commands are executed by this synthesis command: ice40_opt map_ffs: + dffsr2dff dff2dffe -direct-match $_DFF_* techmap -map +/ice40/cells_map.v opt_const -mux_undef simplemap + ice40_ffinit ice40_ffssr ice40_opt -full map_luts: + abc (only if -abc2) + ice40_opt (only if -abc2) abc -lut 4 clean @@ -2759,7 +3062,6 @@ The following commands are executed by this synthesis command: coarse: synth -run coarse - dff2dffe bram: memory_bram -rules +/xilinx/brams.txt @@ -2772,12 +3074,14 @@ The following commands are executed by this synthesis command: fine: opt -fast -full memory_map + dffsr2dff + dff2dffe opt -full techmap -map +/techmap.v -map +/xilinx/arith_map.v opt -fast map_luts: - abc -lut 5:8 [-dff] + abc -luts 2:2,3,6:5,10,20 [-dff] clean map_cells: @@ -3040,6 +3344,9 @@ cell types. Use for example 'all /$add' for all cell types except $add. -nosat do not check SAT model or run SAT equivalence checking + -noeval + do not check const-eval models + -v print additional debug information to the console @@ -3047,6 +3354,21 @@ cell types. Use for example 'all /$add' for all cell types except $add. create a Verilog test bench to test simlib and write_verilog \end{lstlisting} +\section{torder -- print cells in topological order} +\label{cmd:torder} +\begin{lstlisting}[numbers=left,frame=single] + torder [options] [selection] + +This command prints the selected cells in topological order. + + -stop <cell_type> <cell_port> + do not use the specified cell port in topological sorting + + -noautostop + by default Q outputs of internal FF cells and memory read port outputs + are not used in topological sorting. this option deactivates that. +\end{lstlisting} + \section{trace -- redirect command output to file} \label{cmd:trace} \begin{lstlisting}[numbers=left,frame=single] @@ -3056,6 +3378,22 @@ Execute the specified command, logging all changes the command performs on the design in real time. \end{lstlisting} +\section{tribuf -- infer tri-state buffers} +\label{cmd:tribuf} +\begin{lstlisting}[numbers=left,frame=single] + tribuf [options] [selection] + +This pass transforms $mux cells with 'z' inputs to tristate buffers. + + -merge + merge multiple tri-state buffers driving the same net + into a single buffer. + + -logic + convert tri-state buffers that do not drive output ports + to non-tristate logic. this option implies -merge. +\end{lstlisting} + \section{verific -- load Verilog and VHDL designs using Verific} \label{cmd:verific} \begin{lstlisting}[numbers=left,frame=single] @@ -3191,6 +3529,9 @@ file *.blif when any of this options is used. -param use the non-standard .param statement to write cell parameters + -cname + use the non-standard .cname statement to write cell names + -blackbox write blackbox cells with .blackbox statement. @@ -3526,6 +3867,9 @@ to the initial state. -regs also create '<mod>_n' functions for all registers. + -wires + also create '<mod>_n' functions for all public wires. + -tpl <template_file> use the given template file. the line containing only the token '%%' is replaced with the regular output of this command. diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index ce2f0388..01ada773 100644 --- a/passes/cmds/Makefile.inc +++ b/passes/cmds/Makefile.inc @@ -14,6 +14,7 @@ OBJS += passes/cmds/setattr.o OBJS += passes/cmds/copy.o OBJS += passes/cmds/splice.o OBJS += passes/cmds/scc.o +OBJS += passes/cmds/torder.o OBJS += passes/cmds/logcmd.o OBJS += passes/cmds/tee.o OBJS += passes/cmds/write_file.o diff --git a/passes/cmds/edgetypes.cc b/passes/cmds/edgetypes.cc index 5f062c6e..7b75a009 100644 --- a/passes/cmds/edgetypes.cc +++ b/passes/cmds/edgetypes.cc @@ -52,7 +52,7 @@ struct EdgetypePass : public Pass { for (auto module : design->selected_modules()) { SigMap sigmap(module); - dict<SigBit, pool<std::tuple<IdString, IdString, int>>> bit_sources, bit_sinks; + dict<SigBit, pool<tuple<IdString, IdString, int>>> bit_sources, bit_sinks; pool<std::pair<IdString, IdString>> multibit_ports; for (auto cell : module->selected_cells()) @@ -67,9 +67,9 @@ struct EdgetypePass : public Pass { for (int i = 0; i < GetSize(sig); i++) { if (cell->output(port_name)) - bit_sources[sig[i]].insert(std::tuple<IdString, IdString, int>(cell_type, port_name, i)); + bit_sources[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i)); if (cell->input(port_name)) - bit_sinks[sig[i]].insert(std::tuple<IdString, IdString, int>(cell_type, port_name, i)); + bit_sinks[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i)); } } diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc index f7c65bbd..e2d80d9b 100644 --- a/passes/cmds/plugin.cc +++ b/passes/cmds/plugin.cc @@ -115,7 +115,7 @@ struct PluginPass : public Pass { log("\n"); int max_alias_len = 1; for (auto &it : loaded_plugin_aliases) - max_alias_len = std::max(max_alias_len, GetSize(it.first)); + max_alias_len = max(max_alias_len, GetSize(it.first)); for (auto &it : loaded_plugin_aliases) log("Alias: %-*s %s\n", max_alias_len, it.first.c_str(), it.second.c_str()); } diff --git a/passes/cmds/qwp.cc b/passes/cmds/qwp.cc index 784076c3..8ec815a7 100644 --- a/passes/cmds/qwp.cc +++ b/passes/cmds/qwp.cc @@ -342,8 +342,8 @@ struct QwpWorker double r = alt_mode ? alt_radius : radius; if (std::isfinite(v)) { - v = std::min(v, c+r); - v = std::max(v, c-r); + v = min(v, c+r); + v = max(v, c-r); } else { v = c; } @@ -524,15 +524,15 @@ struct QwpWorker if (rel_pos < 0) { node.pos = midpos + left_scale*rel_pos; if (std::isfinite(node.pos)) { - node.pos = std::min(node.pos, midpos); - node.pos = std::max(node.pos, midpos - radius); + node.pos = min(node.pos, midpos); + node.pos = max(node.pos, midpos - radius); } else node.pos = midpos - radius/2; } else { node.pos = midpos + right_scale*rel_pos; if (std::isfinite(node.pos)) { - node.pos = std::max(node.pos, midpos); - node.pos = std::min(node.pos, midpos + radius); + node.pos = max(node.pos, midpos); + node.pos = min(node.pos, midpos + radius); } else node.pos = midpos + radius/2; } @@ -666,8 +666,8 @@ struct QwpWorker double max_value = values.front(); for (auto &v : values) { - min_value = std::min(min_value, v); - max_value = std::max(max_value, v); + min_value = min(min_value, v); + max_value = max(max_value, v); } if (fabs(max_value - min_value) < 0.001) { @@ -679,8 +679,8 @@ struct QwpWorker int max_bucket_val = 0; for (auto &v : values) { - int idx = std::min(int(GetSize(buckets) * (v - min_value) / (max_value - min_value)), GetSize(buckets)-1); - max_bucket_val = std::max(max_bucket_val, ++buckets.at(idx)); + int idx = min(int(GetSize(buckets) * (v - min_value) / (max_value - min_value)), GetSize(buckets)-1); + max_bucket_val = max(max_bucket_val, ++buckets.at(idx)); } for (int i = 4; i >= 0; i--) { diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index 43a43b4f..532026f2 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -69,10 +69,10 @@ struct SccWorker for (auto nextCell : cellToNextCell[cell]) if (cellLabels.count(nextCell) == 0) { run(nextCell, depth+1, maxDepth); - cellLabels[cell].second = std::min(cellLabels[cell].second, cellLabels[nextCell].second); + cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second); } else if (cellsOnStack.count(nextCell) > 0 && (maxDepth < 0 || cellDepth[nextCell] + maxDepth > depth)) { - cellLabels[cell].second = std::min(cellLabels[cell].second, cellLabels[nextCell].second); + cellLabels[cell].second = min(cellLabels[cell].second, cellLabels[nextCell].second); } if (cellLabels[cell].first == cellLabels[cell].second) diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index d05000eb..3e64dd84 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -180,6 +180,47 @@ static void select_op_neg(RTLIL::Design *design, RTLIL::Selection &lhs) lhs.selected_members.swap(new_sel.selected_members); } +static int my_xorshift32_rng() { + static uint32_t x32 = 314159265; + x32 ^= x32 << 13; + x32 ^= x32 >> 17; + x32 ^= x32 << 5; + return x32 & 0x0fffffff; +} + +static void select_op_random(RTLIL::Design *design, RTLIL::Selection &lhs, int count) +{ + vector<pair<IdString, IdString>> objects; + + for (auto mod : design->modules()) + { + if (!lhs.selected_module(mod->name)) + continue; + + for (auto cell : mod->cells()) { + if (lhs.selected_member(mod->name, cell->name)) + objects.push_back(make_pair(mod->name, cell->name)); + } + + for (auto wire : mod->wires()) { + if (lhs.selected_member(mod->name, wire->name)) + objects.push_back(make_pair(mod->name, wire->name)); + } + } + + lhs = RTLIL::Selection(false); + + while (!objects.empty() && count-- > 0) + { + int idx = my_xorshift32_rng() % GetSize(objects); + lhs.selected_members[objects[idx].first].insert(objects[idx].second); + objects[idx] = objects.back(); + objects.pop_back(); + } + + lhs.optimize(design); +} + static void select_op_submod(RTLIL::Design *design, RTLIL::Selection &lhs) { for (auto &mod_it : design->modules_) @@ -634,6 +675,12 @@ static void select_stmt(RTLIL::Design *design, std::string arg) select_op_intersect(design, work_stack[work_stack.size()-2], work_stack[work_stack.size()-1]); work_stack.pop_back(); } else + if (arg.size() >= 2 && arg[0] == '%' && arg[1] == 'R') { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%R.\n"); + int count = arg.size() > 2 ? atoi(arg.c_str() + 2) : 1; + select_op_random(design, work_stack[work_stack.size()-1], count); + } else if (arg == "%s") { if (work_stack.size() < 1) log_cmd_error("Must have at least one element on the stack for operator %%s.\n"); @@ -1100,6 +1147,9 @@ struct SelectPass : public Pass { log(" %%C\n"); log(" select cells that implement selected modules\n"); log("\n"); + log(" %%R[<num>]\n"); + log(" select <num> random objects from top selection (default 1)\n"); + log("\n"); log("Example: the following command selects all wires that are connected to a\n"); log("'GATE' input of a 'SWITCH' cell:\n"); log("\n"); diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 3035e730..3b03d680 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -552,7 +552,7 @@ struct ShowWorker continue; if (design->selected_whole_module(module->name)) { if (module->get_bool_attribute("\\blackbox")) { - log("Skipping blackbox module %s.\n", id2cstr(module->name)); + // log("Skipping blackbox module %s.\n", id2cstr(module->name)); continue; } else if (module->cells_.empty() && module->connections().empty() && module->processes.empty()) { diff --git a/passes/cmds/splice.cc b/passes/cmds/splice.cc index 4ce2ec11..2556fb74 100644 --- a/passes/cmds/splice.cc +++ b/passes/cmds/splice.cc @@ -64,7 +64,7 @@ struct SpliceWorker return sliced_signals_cache.at(sig); int offset = 0; - int p = driven_bits_map.at(sig.extract(0, 1).to_single_sigbit()) - 1; + int p = driven_bits_map.at(sig.extract(0, 1).as_bit()) - 1; while (driven_bits.at(p) != RTLIL::State::Sm) p--, offset++; diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc index 3b6ad014..0d7892d7 100644 --- a/passes/cmds/splitnets.cc +++ b/passes/cmds/splitnets.cc @@ -130,6 +130,9 @@ struct SplitnetsPass : public Pass { } extra_args(args, argidx, design); + // module_ports_db[module_name][old_port_name] = new_port_name_list + dict<IdString, dict<IdString, vector<IdString>>> module_ports_db; + for (auto module : design->selected_modules()) { SplitnetsWorker worker; @@ -140,8 +143,8 @@ struct SplitnetsPass : public Pass { for (auto wire : module->wires()) if (wire->port_id != 0) { - normalized_port_factor = std::max(normalized_port_factor, wire->port_id+1); - normalized_port_factor = std::max(normalized_port_factor, GetSize(wire)+1); + normalized_port_factor = max(normalized_port_factor, wire->port_id+1); + normalized_port_factor = max(normalized_port_factor, GetSize(wire)+1); } for (auto wire : module->wires()) @@ -199,6 +202,26 @@ struct SplitnetsPass : public Pass { module->rewrite_sigspecs(worker); + if (flag_ports) + { + for (auto wire : module->wires()) + { + if (wire->port_id == 0) + continue; + + SigSpec sig(wire); + worker(sig); + + if (sig == wire) + continue; + + vector<IdString> &new_ports = module_ports_db[module->name][wire->name]; + + for (SigSpec c : sig.chunks()) + new_ports.push_back(c.as_wire()->name); + } + } + pool<RTLIL::Wire*> delete_wires; for (auto &it : worker.splitmap) delete_wires.insert(it.first); @@ -207,6 +230,40 @@ struct SplitnetsPass : public Pass { if (flag_ports) module->fixup_ports(); } + + if (!module_ports_db.empty()) + { + for (auto module : design->modules()) + for (auto cell : module->cells()) + { + if (module_ports_db.count(cell->type) == 0) + continue; + + for (auto &it : module_ports_db.at(cell->type)) + { + IdString port_id = it.first; + const auto &new_port_ids = it.second; + + if (!cell->hasPort(port_id)) + continue; + + int offset = 0; + SigSpec sig = cell->getPort(port_id); + + for (auto nid : new_port_ids) + { + int nlen = GetSize(design->module(cell->type)->wire(nid)); + if (offset + nlen > GetSize(sig)) + nlen = GetSize(sig) - offset; + if (nlen > 0) + cell->setPort(nid, sig.extract(offset, nlen)); + offset += nlen; + } + + cell->unsetPort(port_id); + } + } + } } } SplitnetsPass; diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index 0aa76467..048933fc 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -19,6 +19,8 @@ #include "kernel/register.h" #include "kernel/celltypes.h" +#include "passes/techmap/libparse.h" + #include "kernel/log.h" USING_YOSYS_NAMESPACE @@ -29,17 +31,21 @@ struct statdata_t #define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \ X(num_memories) X(num_memory_bits) X(num_cells) X(num_processes) + #define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area) + #define X(_name) int _name; STAT_INT_MEMBERS #undef X + double area; std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type; + std::set<RTLIL::IdString> unknown_cell_area; statdata_t operator+(const statdata_t &other) const { statdata_t sum = other; #define X(_name) sum._name += _name; - STAT_INT_MEMBERS + STAT_NUMERIC_MEMBERS #undef X for (auto &it : num_cells_by_type) sum.num_cells_by_type[it.first] += it.second; @@ -50,7 +56,7 @@ struct statdata_t { statdata_t sum = *this; #define X(_name) sum._name *= other; - STAT_INT_MEMBERS + STAT_NUMERIC_MEMBERS #undef X for (auto &it : sum.num_cells_by_type) it.second *= other; @@ -60,14 +66,14 @@ struct statdata_t statdata_t() { #define X(_name) _name = 0; - STAT_INT_MEMBERS + STAT_NUMERIC_MEMBERS #undef X } - statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode) + statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area) { #define X(_name) _name = 0; - STAT_INT_MEMBERS + STAT_NUMERIC_MEMBERS #undef X for (auto &it : mod->wires_) @@ -110,7 +116,7 @@ struct statdata_t int width_a = it.second->hasPort("\\A") ? GetSize(it.second->getPort("\\A")) : 0; int width_b = it.second->hasPort("\\B") ? GetSize(it.second->getPort("\\B")) : 0; int width_y = it.second->hasPort("\\Y") ? GetSize(it.second->getPort("\\Y")) : 0; - cell_type = stringf("%s_%d", cell_type.c_str(), std::max<int>({width_a, width_b, width_y})); + cell_type = stringf("%s_%d", cell_type.c_str(), max<int>({width_a, width_b, width_y})); } else if (cell_type.in("$mux", "$pmux")) cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Y"))); @@ -118,6 +124,13 @@ struct statdata_t cell_type = stringf("%s_%d", cell_type.c_str(), GetSize(it.second->getPort("\\Q"))); } + if (!cell_area.empty()) { + if (cell_area.count(cell_type)) + area += cell_area.at(cell_type); + else + unknown_cell_area.insert(cell_type); + } + num_cells++; num_cells_by_type[cell_type]++; } @@ -141,6 +154,17 @@ struct statdata_t 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 (!unknown_cell_area.empty()) { + log("\n"); + for (auto cell_type : unknown_cell_area) + log(" Area for cell type %s is unknown!\n", cell_type.c_str()); + } + + if (area != 0) { + log("\n"); + log(" Chip area for this module: %f\n", area); + } } }; @@ -162,6 +186,26 @@ statdata_t hierarchy_worker(std::map<RTLIL::IdString, statdata_t> &mod_stat, RTL return mod_data; } +void read_liberty_cellarea(dict<IdString, double> &cell_area, string liberty_file) +{ + std::ifstream f; + f.open(liberty_file.c_str()); + if (f.fail()) + log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno)); + LibertyParser libparser(f); + f.close(); + + for (auto cell : libparser.ast->children) + { + if (cell->id != "cell" || cell->args.size() != 1) + continue; + + LibertyAst *ar = cell->find("area"); + if (ar != NULL && !ar->value.empty()) + cell_area["\\" + cell->args[0]] = atof(ar->value.c_str()); + } +} + struct StatPass : public Pass { StatPass() : Pass("stat", "print some statistics") { } virtual void help() @@ -178,6 +222,9 @@ struct StatPass : public Pass { log(" selected and a module has the 'top' attribute set, this module is used\n"); log(" default value for this option.\n"); log("\n"); + log(" -liberty <liberty_file>\n"); + log(" use cell area information from the provided liberty file\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"); @@ -190,6 +237,7 @@ struct StatPass : public Pass { bool width_mode = false; RTLIL::Module *top_mod = NULL; std::map<RTLIL::IdString, statdata_t> mod_stat; + dict<IdString, double> cell_area; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -198,6 +246,12 @@ struct StatPass : public Pass { width_mode = true; continue; } + if (args[argidx] == "-liberty" && argidx+1 < args.size()) { + string liberty_file = args[++argidx]; + rewrite_filename(liberty_file); + read_liberty_cellarea(cell_area, liberty_file); + 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()); @@ -214,7 +268,7 @@ struct StatPass : public Pass { if (mod->get_bool_attribute("\\top")) top_mod = mod; - statdata_t data(design, mod, width_mode); + statdata_t data(design, mod, width_mode, cell_area); mod_stat[mod->name] = data; log("\n"); @@ -223,7 +277,7 @@ struct StatPass : public Pass { data.log_data(); } - if (top_mod != NULL) + if (top_mod != NULL && GetSize(mod_stat) > 1) { log("\n"); log("=== design hierarchy ===\n"); diff --git a/passes/cmds/torder.cc b/passes/cmds/torder.cc new file mode 100644 index 00000000..50317c02 --- /dev/null +++ b/passes/cmds/torder.cc @@ -0,0 +1,123 @@ +/* + * 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/celltypes.h" +#include "kernel/sigtools.h" +#include "kernel/utils.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct TorderPass : public Pass { + TorderPass() : Pass("torder", "print cells in topological order") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" torder [options] [selection]\n"); + log("\n"); + log("This command prints the selected cells in topological order.\n"); + log("\n"); + log(" -stop <cell_type> <cell_port>\n"); + log(" do not use the specified cell port in topological sorting\n"); + log("\n"); + log(" -noautostop\n"); + log(" by default Q outputs of internal FF cells and memory read port outputs\n"); + log(" are not used in topological sorting. this option deactivates that.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + bool noautostop = false; + dict<IdString, pool<IdString>> stop_db; + + log_header("Executing TORDER pass (print cells in topological order).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-stop" && argidx+2 < args.size()) { + IdString cell_type = RTLIL::escape_id(args[++argidx]); + IdString cell_port = RTLIL::escape_id(args[++argidx]); + stop_db[cell_type].insert(cell_port); + continue; + } + if (args[argidx] == "-noautostop") { + noautostop = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + log("module %s\n", log_id(module)); + + SigMap sigmap(module); + dict<SigBit, pool<IdString>> bit_drivers, bit_users; + TopoSort<IdString, RTLIL::sort_by_id_str> toposort; + + for (auto cell : module->selected_cells()) + for (auto conn : cell->connections()) + { + if (stop_db.count(cell->type) && stop_db.at(cell->type).count(conn.first)) + continue; + + if (!noautostop && yosys_celltypes.cell_known(cell->type)) { + if (conn.first.in("\\Q", "\\CTRL_OUT", "\\RD_DATA")) + continue; + if (cell->type == "$memrd" && conn.first == "\\DATA") + continue; + } + + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + bit_users[bit].insert(cell->name); + + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + bit_drivers[bit].insert(cell->name); + + toposort.node(cell->name); + } + + for (auto &it : bit_users) + if (bit_drivers.count(it.first)) + for (auto driver_cell : bit_drivers.at(it.first)) + for (auto user_cell : it.second) + toposort.edge(driver_cell, user_cell); + + toposort.analyze_loops = true; + toposort.sort(); + + for (auto &it : toposort.loops) { + log(" loop"); + for (auto cell : it) + log(" %s", log_id(cell)); + log("\n"); + } + + for (auto cell : toposort.sorted) + log(" cell %s\n", log_id(cell)); + } + } +} TorderPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/equiv/Makefile.inc b/passes/equiv/Makefile.inc index 548eaca3..dd7b3be0 100644 --- a/passes/equiv/Makefile.inc +++ b/passes/equiv/Makefile.inc @@ -6,4 +6,7 @@ OBJS += passes/equiv/equiv_status.o OBJS += passes/equiv/equiv_add.o OBJS += passes/equiv/equiv_remove.o OBJS += passes/equiv/equiv_induct.o +OBJS += passes/equiv/equiv_struct.o +OBJS += passes/equiv/equiv_purge.o +OBJS += passes/equiv/equiv_mark.o diff --git a/passes/equiv/equiv_add.cc b/passes/equiv/equiv_add.cc index 4ce750b1..0494a724 100644 --- a/passes/equiv/equiv_add.cc +++ b/passes/equiv/equiv_add.cc @@ -29,15 +29,19 @@ struct EquivAddPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" equiv_add gold_sig gate_sig\n"); + log(" equiv_add [-try] gold_sig gate_sig\n"); log("\n"); log("This command adds an $equiv cell for the specified signals.\n"); log("\n"); + log("\n"); + log(" equiv_add [-try] -cell gold_cell gate_cell\n"); + log("\n"); + log("This command adds $equiv cells for the ports of the specified cells.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, Design *design) { - if (GetSize(args) != 3) - cmd_error(args, GetSize(args)-1, "Invalid number of arguments."); + bool try_mode = false; if (design->selected_active_module.empty()) log_cmd_error("This command must be executed in module context!\n"); @@ -45,44 +49,128 @@ struct EquivAddPass : public Pass { Module *module = design->module(design->selected_active_module); log_assert(module != nullptr); - SigSpec gold_signal, gate_signal; + if (GetSize(args) > 1 && args[1] == "-try") { + args.erase(args.begin() + 1); + try_mode = true; + } - if (!SigSpec::parse(gate_signal, module, args[2])) - log_cmd_error("Error in gate signal: %s\n", args[2].c_str()); + if (GetSize(args) == 4 && args[1] == "-cell") + { + Cell *gold_cell = module->cell(RTLIL::escape_id(args[2])); + Cell *gate_cell = module->cell(RTLIL::escape_id(args[3])); - if (!SigSpec::parse_rhs(gate_signal, gold_signal, module, args[1])) - log_cmd_error("Error in gold signal: %s\n", args[1].c_str()); + if (gold_cell == nullptr) { + if (try_mode) { + log_warning("Can't find gold cell '%s'.\n", args[2].c_str()); + return; + } + log_cmd_error("Can't find gold cell '%s'.\n", args[2].c_str()); + } - log_assert(GetSize(gold_signal) == GetSize(gate_signal)); - SigSpec equiv_signal = module->addWire(NEW_ID, GetSize(gold_signal)); + if (gate_cell == nullptr) { + if (try_mode) { + log_warning("Can't find gate cell '%s'.\n", args[3].c_str()); + return; + } + log_cmd_error("Can't find gate cell '%s'.\n", args[3].c_str()); + } - SigMap sigmap(module); - sigmap.apply(gold_signal); - sigmap.apply(gate_signal); + for (auto conn : gold_cell->connections()) + { + auto port = conn.first; + SigSpec gold_sig = gold_cell->getPort(port); + SigSpec gate_sig = gate_cell->getPort(port); + int width = min(GetSize(gold_sig), GetSize(gate_sig)); - dict<SigBit, SigBit> to_equiv_bits; - pool<Cell*> added_equiv_cells; + if (gold_cell->input(port) && gate_cell->input(port)) + { + SigSpec combined_sig = module->addWire(NEW_ID, width); - for (int i = 0; i < GetSize(gold_signal); i++) { - Cell *equiv_cell = module->addEquiv(NEW_ID, gold_signal[i], gate_signal[i], equiv_signal[i]); - equiv_cell->set_bool_attribute("\\keep"); - to_equiv_bits[gold_signal[i]] = equiv_signal[i]; - to_equiv_bits[gate_signal[i]] = equiv_signal[i]; - added_equiv_cells.insert(equiv_cell); + for (int i = 0; i < width; i++) { + module->addEquiv(NEW_ID, gold_sig[i], gate_sig[i], combined_sig[i]); + gold_sig[i] = gate_sig[i] = combined_sig[i]; + } + + gold_cell->setPort(port, gold_sig); + gate_cell->setPort(port, gate_sig); + continue; + } + + if (gold_cell->output(port) && gate_cell->output(port)) + { + SigSpec new_gold_wire = module->addWire(NEW_ID, width); + SigSpec new_gate_wire = module->addWire(NEW_ID, width); + SigSig gg_conn; + + for (int i = 0; i < width; i++) { + module->addEquiv(NEW_ID, new_gold_wire[i], new_gold_wire[i], gold_sig[i]); + gg_conn.first.append(gate_sig[i]); + gg_conn.second.append(gold_sig[i]); + gold_sig[i] = new_gold_wire[i]; + gate_sig[i] = new_gate_wire[i]; + } + + module->connect(gg_conn); + gold_cell->setPort(port, gold_sig); + gate_cell->setPort(port, gate_sig); + continue; + } + } } + else + { + if (GetSize(args) != 3) + cmd_error(args, GetSize(args)-1, "Invalid number of arguments."); - for (auto cell : module->cells()) - for (auto conn : cell->connections()) - if (!added_equiv_cells.count(cell) && cell->input(conn.first)) { - SigSpec new_sig; - for (auto bit : conn.second) - if (to_equiv_bits.count(sigmap(bit))) - new_sig.append(to_equiv_bits.at(sigmap(bit))); - else - new_sig.append(bit); - if (conn.second != new_sig) - cell->setPort(conn.first, new_sig); + SigSpec gold_signal, gate_signal; + + if (!SigSpec::parse(gate_signal, module, args[2])) { + if (try_mode) { + log_warning("Error in gate signal: %s\n", args[2].c_str()); + return; + } + log_cmd_error("Error in gate signal: %s\n", args[2].c_str()); + } + + if (!SigSpec::parse_rhs(gate_signal, gold_signal, module, args[1])) { + if (try_mode) { + log_warning("Error in gold signal: %s\n", args[1].c_str()); + return; + } + log_cmd_error("Error in gold signal: %s\n", args[1].c_str()); + } + + log_assert(GetSize(gold_signal) == GetSize(gate_signal)); + SigSpec equiv_signal = module->addWire(NEW_ID, GetSize(gold_signal)); + + SigMap sigmap(module); + sigmap.apply(gold_signal); + sigmap.apply(gate_signal); + + dict<SigBit, SigBit> to_equiv_bits; + pool<Cell*> added_equiv_cells; + + for (int i = 0; i < GetSize(gold_signal); i++) { + Cell *equiv_cell = module->addEquiv(NEW_ID, gold_signal[i], gate_signal[i], equiv_signal[i]); + equiv_cell->set_bool_attribute("\\keep"); + to_equiv_bits[gold_signal[i]] = equiv_signal[i]; + to_equiv_bits[gate_signal[i]] = equiv_signal[i]; + added_equiv_cells.insert(equiv_cell); } + + for (auto cell : module->cells()) + for (auto conn : cell->connections()) + if (!added_equiv_cells.count(cell) && cell->input(conn.first)) { + SigSpec new_sig; + for (auto bit : conn.second) + if (to_equiv_bits.count(sigmap(bit))) + new_sig.append(to_equiv_bits.at(sigmap(bit))); + else + new_sig.append(bit); + if (conn.second != new_sig) + cell->setPort(conn.first, new_sig); + } + } } } EquivAddPass; diff --git a/passes/equiv/equiv_induct.cc b/passes/equiv/equiv_induct.cc index a536fe30..cdb951ec 100644 --- a/passes/equiv/equiv_induct.cc +++ b/passes/equiv/equiv_induct.cc @@ -59,8 +59,8 @@ struct EquivInductWorker cell_warn_cache.insert(cell); } if (cell->type == "$equiv") { - SigBit bit_a = sigmap(cell->getPort("\\A")).to_single_sigbit(); - SigBit bit_b = sigmap(cell->getPort("\\B")).to_single_sigbit(); + SigBit bit_a = sigmap(cell->getPort("\\A")).as_bit(); + SigBit bit_b = sigmap(cell->getPort("\\B")).as_bit(); if (bit_a != bit_b) { int ez_a = satgen.importSigBit(bit_a, step); int ez_b = satgen.importSigBit(bit_b, step); @@ -137,8 +137,8 @@ struct EquivInductWorker for (auto cell : workset) { - SigBit bit_a = sigmap(cell->getPort("\\A")).to_single_sigbit(); - SigBit bit_b = sigmap(cell->getPort("\\B")).to_single_sigbit(); + SigBit bit_a = sigmap(cell->getPort("\\A")).as_bit(); + SigBit bit_b = sigmap(cell->getPort("\\B")).as_bit(); log(" Trying to prove $equiv for %s:", log_signal(sigmap(cell->getPort("\\Y")))); diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc index c001fdbf..8b063c54 100644 --- a/passes/equiv/equiv_make.cc +++ b/passes/equiv/equiv_make.cc @@ -280,7 +280,7 @@ struct EquivMakeWorker for (auto c : cells_list) for (auto &conn : c->connections()) - if (ct.cell_input(c->type, conn.first)) { + if (!ct.cell_output(c->type, conn.first)) { SigSpec old_sig = assign_map(conn.second); SigSpec new_sig = rd_signal_map(old_sig); if (old_sig != new_sig) { diff --git a/passes/equiv/equiv_mark.cc b/passes/equiv/equiv_mark.cc new file mode 100644 index 00000000..3e9819d1 --- /dev/null +++ b/passes/equiv/equiv_mark.cc @@ -0,0 +1,239 @@ +/* + * 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 + +struct EquivMarkWorker +{ + Module *module; + SigMap sigmap; + + // cache for traversing signal flow graph + dict<SigBit, pool<IdString>> up_bit2cells; + dict<IdString, pool<SigBit>> up_cell2bits; + pool<IdString> edge_cells, equiv_cells; + + // graph traversal state + pool<SigBit> queue, visited; + + // assigned regions + dict<IdString, int> cell_regions; + dict<SigBit, int> bit_regions; + int next_region; + + // merge-find + mfp<int> region_mf; + + EquivMarkWorker(Module *module) : module(module), sigmap(module) + { + for (auto cell : module->cells()) + { + if (cell->type == "$equiv") + equiv_cells.insert(cell->name); + + for (auto &port : cell->connections()) + { + if (cell->input(port.first)) + for (auto bit : sigmap(port.second)) + up_cell2bits[cell->name].insert(bit); + + if (cell->output(port.first)) + for (auto bit : sigmap(port.second)) + up_bit2cells[bit].insert(cell->name); + } + } + + next_region = 0; + } + + void mark() + { + while (!queue.empty()) + { + pool<IdString> cells; + + for (auto &bit : queue) + { + // log_assert(bit_regions.count(bit) == 0); + bit_regions[bit] = next_region; + visited.insert(bit); + + for (auto cell : up_bit2cells[bit]) + if (edge_cells.count(cell) == 0) + cells.insert(cell); + } + + queue.clear(); + + for (auto cell : cells) + { + if (next_region == 0 && equiv_cells.count(cell)) + continue; + + if (cell_regions.count(cell)) { + if (cell_regions.at(cell) != 0) + region_mf.merge(cell_regions.at(cell), next_region); + continue; + } + + cell_regions[cell] = next_region; + + for (auto bit : up_cell2bits[cell]) + if (visited.count(bit) == 0) + queue.insert(bit); + } + } + + next_region++; + } + + void run() + { + log("Running equiv_mark on module %s:\n", log_id(module)); + + // marking region 0 + + for (auto wire : module->wires()) + if (wire->port_id > 0) + for (auto bit : sigmap(wire)) + queue.insert(bit); + + for (auto cell_name : equiv_cells) + { + auto cell = module->cell(cell_name); + + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + + if (sig_a == sig_b) { + for (auto bit : sig_a) + queue.insert(bit); + edge_cells.insert(cell_name); + cell_regions[cell_name] = 0; + } + } + + mark(); + + // marking unsolved regions + + for (auto cell : module->cells()) + { + if (cell_regions.count(cell->name) || cell->type != "$equiv") + continue; + + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + + log_assert(sig_a != sig_b); + + for (auto bit : sig_a) + queue.insert(bit); + + for (auto bit : sig_b) + queue.insert(bit); + + cell_regions[cell->name] = next_region; + mark(); + } + + // setting attributes + + dict<int, int> final_region_map; + int next_final_region = 0; + + dict<int, int> region_cell_count; + dict<int, int> region_wire_count; + + for (int i = 0; i < next_region; i++) { + int r = region_mf.find(i); + if (final_region_map.count(r) == 0) + final_region_map[r] = next_final_region++; + final_region_map[i] = final_region_map[r]; + } + + for (auto cell : module->cells()) + { + if (cell_regions.count(cell->name)) { + int r = final_region_map.at(cell_regions.at(cell->name)); + cell->attributes["\\equiv_region"] = Const(r); + region_cell_count[r]++; + } else + cell->attributes.erase("\\equiv_region"); + } + + for (auto wire : module->wires()) + { + pool<int> regions; + for (auto bit : sigmap(wire)) + if (bit_regions.count(bit)) + regions.insert(region_mf.find(bit_regions.at(bit))); + + if (GetSize(regions) == 1) { + int r = final_region_map.at(*regions.begin()); + wire->attributes["\\equiv_region"] = Const(r); + region_wire_count[r]++; + } else + wire->attributes.erase("\\equiv_region"); + } + + for (int i = 0; i < next_final_region; i++) + log(" region %d: %d cells, %d wires\n", i, region_wire_count[i], region_cell_count[i]); + } +}; + +struct EquivMarkPass : public Pass { + EquivMarkPass() : Pass("equiv_mark", "mark equivalence checking regions") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_mark [options] [selection]\n"); + log("\n"); + log("This command marks the regions in an equivalence checking module. Region 0 is\n"); + log("the proven part of the circuit. Regions with higher numbers are connected\n"); + log("unproven subcricuits. The integer attribute 'equiv_region' is set on all\n"); + log("wires and cells.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + log_header("Executing EQUIV_MARK pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // if (args[argidx] == "-foobar") { + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_whole_modules_warn()) { + EquivMarkWorker worker(module); + worker.run(); + } + } +} EquivMarkPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/equiv/equiv_miter.cc b/passes/equiv/equiv_miter.cc index 34318dec..982176c4 100644 --- a/passes/equiv/equiv_miter.cc +++ b/passes/equiv/equiv_miter.cc @@ -156,7 +156,7 @@ struct EquivMiterWorker struct RewriteSigSpecWorker { RTLIL::Module * mod; void operator()(SigSpec &sig) { - vector<RTLIL::SigChunk> chunks = sig.chunks(); + vector<SigChunk> chunks = sig.chunks(); for (auto &c : chunks) if (c.wire != NULL) c.wire = mod->wires_.at(c.wire->name); diff --git a/passes/equiv/equiv_purge.cc b/passes/equiv/equiv_purge.cc new file mode 100644 index 00000000..f4141ad4 --- /dev/null +++ b/passes/equiv/equiv_purge.cc @@ -0,0 +1,210 @@ +/* + * 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 + +struct EquivPurgeWorker +{ + Module *module; + SigMap sigmap; + int name_cnt; + + EquivPurgeWorker(Module *module) : module(module), sigmap(module), name_cnt(0) { } + + SigSpec make_output(SigSpec sig, IdString cellname) + { + if (sig.is_wire()) { + Wire *wire = sig.as_wire(); + if (wire->name[0] == '\\') { + if (!wire->port_output) { + log(" Module output: %s (%s)\n", log_signal(wire), log_id(cellname)); + wire->port_output = true; + } + return wire; + } + } + + while (1) + { + IdString name = stringf("\\equiv_%d", name_cnt++); + if (module->count_id(name)) + continue; + + Wire *wire = module->addWire(name, GetSize(sig)); + wire->port_output = true; + module->connect(wire, sig); + log(" Module output: %s (%s)\n", log_signal(wire), log_id(cellname)); + return wire; + } + } + + SigSpec make_input(SigSpec sig) + { + if (sig.is_wire()) { + Wire *wire = sig.as_wire(); + if (wire->name[0] == '\\') { + if (!wire->port_output) { + log(" Module input: %s\n", log_signal(wire)); + wire->port_input = true; + } + return module->addWire(NEW_ID, GetSize(sig)); + } + } + + while (1) + { + IdString name = stringf("\\equiv_%d", name_cnt++); + if (module->count_id(name)) + continue; + + Wire *wire = module->addWire(name, GetSize(sig)); + wire->port_input = true; + module->connect(sig, wire); + log(" Module input: %s\n", log_signal(wire)); + return module->addWire(NEW_ID, GetSize(sig)); + } + } + + void run() + { + log("Running equiv_purge on module %s:\n", log_id(module)); + + for (auto wire : module->wires()) { + wire->port_input = false; + wire->port_output = false; + } + + pool<SigBit> queue, visited; + + // cache for traversing signal flow graph + dict<SigBit, pool<IdString>> up_bit2cells; + dict<IdString, pool<SigBit>> up_cell2bits; + + for (auto cell : module->cells()) + { + if (cell->type != "$equiv") { + for (auto &port : cell->connections()) { + if (cell->input(port.first)) + for (auto bit : sigmap(port.second)) + up_cell2bits[cell->name].insert(bit); + if (cell->output(port.first)) + for (auto bit : sigmap(port.second)) + up_bit2cells[bit].insert(cell->name); + } + continue; + } + + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + SigSpec sig_y = sigmap(cell->getPort("\\Y")); + + if (sig_a == sig_b) + continue; + + for (auto bit : sig_a) + queue.insert(bit); + + for (auto bit : sig_b) + queue.insert(bit); + + for (auto bit : sig_y) + visited.insert(bit); + + cell->setPort("\\Y", make_output(sig_y, cell->name)); + } + + SigSpec srcsig; + SigMap rewrite_sigmap(module); + + while (!queue.empty()) + { + pool<SigBit> next_queue; + + for (auto bit : queue) + visited.insert(bit); + + for (auto bit : queue) + { + auto &cells = up_bit2cells[bit]; + + if (cells.empty()) { + srcsig.append(bit); + } else { + for (auto cell : cells) + for (auto bit : up_cell2bits[cell]) + if (visited.count(bit) == 0) + next_queue.insert(bit); + } + } + + next_queue.swap(queue); + } + + srcsig.sort_and_unify(); + + for (SigChunk chunk : srcsig.chunks()) + if (chunk.wire != nullptr) + rewrite_sigmap.add(chunk, make_input(chunk)); + + for (auto cell : module->cells()) + if (cell->type == "$equiv") + cell->setPort("\\Y", rewrite_sigmap(sigmap(cell->getPort("\\Y")))); + + module->fixup_ports(); + } +}; + +struct EquivPurgePass : public Pass { + EquivPurgePass() : Pass("equiv_purge", "purge equivalence checking module") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_purge [options] [selection]\n"); + log("\n"); + log("This command removes the proven part of an equivalence checking module, leaving\n"); + log("only the unproven segments in the design. This will also remove and add module\n"); + log("ports as needed.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + log_header("Executing EQUIV_PURGE pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // if (args[argidx] == "-foobar") { + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_whole_modules_warn()) { + EquivPurgeWorker worker(module); + worker.run(); + } + } +} EquivPurgePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/equiv/equiv_simple.cc b/passes/equiv/equiv_simple.cc index 1f52a632..fa22dc62 100644 --- a/passes/equiv/equiv_simple.cc +++ b/passes/equiv/equiv_simple.cc @@ -89,8 +89,8 @@ struct EquivSimpleWorker bool run_cell() { - SigBit bit_a = sigmap(equiv_cell->getPort("\\A")).to_single_sigbit(); - SigBit bit_b = sigmap(equiv_cell->getPort("\\B")).to_single_sigbit(); + SigBit bit_a = sigmap(equiv_cell->getPort("\\A")).as_bit(); + SigBit bit_b = sigmap(equiv_cell->getPort("\\B")).as_bit(); int ez_context = ez->frozen_literal(); if (satgen.model_undef) @@ -314,7 +314,7 @@ struct EquivSimplePass : public Pass { for (auto cell : module->selected_cells()) if (cell->type == "$equiv" && cell->getPort("\\A") != cell->getPort("\\B")) { - auto bit = sigmap(cell->getPort("\\Y").to_single_sigbit()); + auto bit = sigmap(cell->getPort("\\Y").as_bit()); auto bit_group = bit; if (!nogroup && bit_group.wire) bit_group.offset = 0; diff --git a/passes/equiv/equiv_struct.cc b/passes/equiv/equiv_struct.cc new file mode 100644 index 00000000..2c85d2d3 --- /dev/null +++ b/passes/equiv/equiv_struct.cc @@ -0,0 +1,367 @@ +/* + * 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 + +struct EquivStructWorker +{ + Module *module; + SigMap sigmap; + SigMap equiv_bits; + bool mode_fwd; + bool mode_icells; + int merge_count; + + const pool<IdString> &fwonly_cells; + + struct merge_key_t + { + IdString type; + vector<pair<IdString, Const>> parameters; + vector<pair<IdString, int>> port_sizes; + vector<tuple<IdString, int, SigBit>> connections; + + bool operator==(const merge_key_t &other) const { + return type == other.type && connections == other.connections && + parameters == other.parameters && port_sizes == other.port_sizes; + } + + unsigned int hash() const { + unsigned int h = mkhash_init; + h = mkhash(h, mkhash(type)); + h = mkhash(h, mkhash(parameters)); + h = mkhash(h, mkhash(connections)); + return h; + } + }; + + dict<merge_key_t, pool<IdString>> merge_cache; + pool<merge_key_t> fwd_merge_cache, bwd_merge_cache; + + void merge_cell_pair(Cell *cell_a, Cell *cell_b) + { + SigMap merged_map; + merge_count++; + + SigSpec inputs_a, inputs_b; + vector<string> input_names; + + for (auto &port_a : cell_a->connections()) + { + SigSpec bits_a = sigmap(port_a.second); + SigSpec bits_b = sigmap(cell_b->getPort(port_a.first)); + + log_assert(GetSize(bits_a) == GetSize(bits_b)); + + if (!cell_a->output(port_a.first)) + for (int i = 0; i < GetSize(bits_a); i++) + if (bits_a[i] != bits_b[i]) { + inputs_a.append(bits_a[i]); + inputs_b.append(bits_b[i]); + input_names.push_back(GetSize(bits_a) == 1 ? port_a.first.str() : + stringf("%s[%d]", log_id(port_a.first), i)); + } + } + + for (int i = 0; i < GetSize(inputs_a); i++) { + SigBit bit_a = inputs_a[i], bit_b = inputs_b[i]; + SigBit bit_y = module->addWire(NEW_ID); + log(" New $equiv for input %s: A: %s, B: %s, Y: %s\n", + input_names[i].c_str(), log_signal(bit_a), log_signal(bit_b), log_signal(bit_y)); + module->addEquiv(NEW_ID, bit_a, bit_b, bit_y); + merged_map.add(bit_a, bit_y); + merged_map.add(bit_b, bit_y); + } + + std::vector<IdString> outport_names, inport_names; + + for (auto &port_a : cell_a->connections()) + if (cell_a->output(port_a.first)) + outport_names.push_back(port_a.first); + else + inport_names.push_back(port_a.first); + + for (auto &pn : inport_names) + cell_a->setPort(pn, merged_map(sigmap(cell_a->getPort(pn)))); + + for (auto &pn : outport_names) { + SigSpec sig_a = cell_a->getPort(pn); + SigSpec sig_b = cell_b->getPort(pn); + module->connect(sig_b, sig_a); + } + + auto merged_attr = cell_b->get_strpool_attribute("\\equiv_merged"); + merged_attr.insert(log_id(cell_b)); + cell_a->add_strpool_attribute("\\equiv_merged", merged_attr); + module->remove(cell_b); + } + + EquivStructWorker(Module *module, bool mode_fwd, bool mode_icells, const pool<IdString> &fwonly_cells, int iter_num) : + module(module), sigmap(module), equiv_bits(module), + mode_fwd(mode_fwd), mode_icells(mode_icells), merge_count(0), fwonly_cells(fwonly_cells) + { + log(" Starting iteration %d.\n", iter_num); + + pool<SigBit> equiv_inputs; + pool<IdString> cells; + + for (auto cell : module->selected_cells()) + if (cell->type == "$equiv") { + SigBit sig_a = sigmap(cell->getPort("\\A").as_bit()); + SigBit sig_b = sigmap(cell->getPort("\\B").as_bit()); + equiv_bits.add(sig_b, sig_a); + equiv_inputs.insert(sig_a); + equiv_inputs.insert(sig_b); + cells.insert(cell->name); + } else { + if (mode_icells || module->design->module(cell->type)) + cells.insert(cell->name); + } + + for (auto cell : module->selected_cells()) + if (cell->type == "$equiv") { + SigBit sig_a = sigmap(cell->getPort("\\A").as_bit()); + SigBit sig_b = sigmap(cell->getPort("\\B").as_bit()); + SigBit sig_y = sigmap(cell->getPort("\\Y").as_bit()); + if (sig_a == sig_b && equiv_inputs.count(sig_y)) { + log(" Purging redundant $equiv cell %s.\n", log_id(cell)); + module->connect(sig_y, sig_a); + module->remove(cell); + merge_count++; + } + } + + if (merge_count > 0) + return; + + for (auto cell_name : cells) + { + merge_key_t key; + vector<tuple<IdString, int, SigBit>> fwd_connections; + + Cell *cell = module->cell(cell_name); + key.type = cell->type; + + for (auto &it : cell->parameters) + key.parameters.push_back(it); + std::sort(key.parameters.begin(), key.parameters.end()); + + for (auto &it : cell->connections()) + key.port_sizes.push_back(make_pair(it.first, GetSize(it.second))); + std::sort(key.port_sizes.begin(), key.port_sizes.end()); + + for (auto &conn : cell->connections()) + { + if (cell->input(conn.first)) { + SigSpec sig = sigmap(conn.second); + for (int i = 0; i < GetSize(sig); i++) + fwd_connections.push_back(make_tuple(conn.first, i, sig[i])); + } + + if (cell->output(conn.first)) { + SigSpec sig = equiv_bits(conn.second); + for (int i = 0; i < GetSize(sig); i++) { + key.connections.clear(); + key.connections.push_back(make_tuple(conn.first, i, sig[i])); + + if (merge_cache.count(key)) + bwd_merge_cache.insert(key); + merge_cache[key].insert(cell_name); + } + } + } + + std::sort(fwd_connections.begin(), fwd_connections.end()); + key.connections.swap(fwd_connections); + + if (merge_cache.count(key)) + fwd_merge_cache.insert(key); + merge_cache[key].insert(cell_name); + } + + for (int phase = 0; phase < 2; phase++) + { + auto &queue = phase ? bwd_merge_cache : fwd_merge_cache; + + for (auto &key : queue) + { + const char *strategy = nullptr; + vector<Cell*> gold_cells, gate_cells, other_cells; + vector<pair<Cell*, Cell*>> cell_pairs; + IdString cells_type; + + for (auto cell_name : merge_cache[key]) { + Cell *c = module->cell(cell_name); + if (c != nullptr) { + string n = cell_name.str(); + cells_type = c->type; + if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gold") + gold_cells.push_back(c); + else if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gate") + gate_cells.push_back(c); + else + other_cells.push_back(c); + } + } + + if (phase && fwonly_cells.count(cells_type)) + continue; + + if (GetSize(gold_cells) > 1 || GetSize(gate_cells) > 1 || GetSize(other_cells) > 1) + { + strategy = "deduplicate"; + for (int i = 0; i+1 < GetSize(gold_cells); i += 2) + cell_pairs.push_back(make_pair(gold_cells[i], gold_cells[i+1])); + for (int i = 0; i+1 < GetSize(gate_cells); i += 2) + cell_pairs.push_back(make_pair(gate_cells[i], gate_cells[i+1])); + for (int i = 0; i+1 < GetSize(other_cells); i += 2) + cell_pairs.push_back(make_pair(other_cells[i], other_cells[i+1])); + goto run_strategy; + } + + if (GetSize(gold_cells) == 1 && GetSize(gate_cells) == 1) + { + strategy = "gold-gate-pairs"; + cell_pairs.push_back(make_pair(gold_cells[0], gate_cells[0])); + goto run_strategy; + } + + if (GetSize(gold_cells) == 1 && GetSize(other_cells) == 1) + { + strategy = "gold-guess"; + cell_pairs.push_back(make_pair(gold_cells[0], other_cells[0])); + goto run_strategy; + } + + if (GetSize(other_cells) == 1 && GetSize(gate_cells) == 1) + { + strategy = "gate-guess"; + cell_pairs.push_back(make_pair(other_cells[0], gate_cells[0])); + goto run_strategy; + } + + log_assert(GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells) < 2); + continue; + + run_strategy: + int total_group_size = GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells); + log(" %s merging %d %s cells (from group of %d) using strategy %s:\n", phase ? "Bwd" : "Fwd", + 2*GetSize(cell_pairs), log_id(cells_type), total_group_size, strategy); + for (auto it : cell_pairs) { + log(" Merging cells %s and %s.\n", log_id(it.first), log_id(it.second)); + merge_cell_pair(it.first, it.second); + } + } + + if (merge_count > 0) + return; + } + + log(" Nothing to merge.\n"); + } +}; + +struct EquivStructPass : public Pass { + EquivStructPass() : Pass("equiv_struct", "structural equivalence checking") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_struct [options] [selection]\n"); + log("\n"); + log("This command adds additional $equiv cells based on the assumption that the\n"); + log("gold and gate circuit are structurally equivalent. Note that this can introduce\n"); + log("bad $equiv cells in cases where the netlists are not structurally equivalent,\n"); + log("for example when analyzing circuits with cells with commutative inputs. This\n"); + log("command will also de-duplicate gates.\n"); + log("\n"); + log(" -fwd\n"); + log(" by default this command performans forward sweeps until nothing can\n"); + log(" be merged by forwards sweeps, then backward sweeps until forward\n"); + log(" sweeps are effective again. with this option set only forward sweeps\n"); + log(" are performed.\n"); + log("\n"); + log(" -fwonly <cell_type>\n"); + log(" add the specified cell type to the list of cell types that are only\n"); + log(" merged in forward sweeps and never in backward sweeps. $equiv is in\n"); + log(" this list automatically.\n"); + log("\n"); + log(" -icells\n"); + log(" by default, the internal RTL and gate cell types are ignored. add\n"); + log(" this option to also process those cell types with this command.\n"); + log("\n"); + log(" -maxiter <N>\n"); + log(" maximum number of iterations to run before aborting\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, Design *design) + { + pool<IdString> fwonly_cells({ "$equiv" }); + bool mode_icells = false; + bool mode_fwd = false; + int max_iter = -1; + + log_header("Executing EQUIV_STRUCT pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-fwd") { + mode_fwd = true; + continue; + } + if (args[argidx] == "-icells") { + mode_icells = true; + continue; + } + if (args[argidx] == "-fwonly" && argidx+1 < args.size()) { + fwonly_cells.insert(RTLIL::escape_id(args[++argidx])); + continue; + } + if (args[argidx] == "-maxiter" && argidx+1 < args.size()) { + max_iter = atoi(args[++argidx].c_str()); + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) { + int module_merge_count = 0; + log("Running equiv_struct on module %s:\n", log_id(module)); + for (int iter = 0;; iter++) { + if (iter == max_iter) { + log(" Reached iteration limit of %d.\n", iter); + break; + } + EquivStructWorker worker(module, mode_fwd, mode_icells, fwonly_cells, iter+1); + if (worker.merge_count == 0) + break; + module_merge_count += worker.merge_count; + } + if (module_merge_count) + log(" Performed a total of %d merges in module %s.\n", module_merge_count, log_id(module)); + } + } +} EquivStructPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc index 7322368c..a7cc95ff 100644 --- a/passes/fsm/fsm_opt.cc +++ b/passes/fsm/fsm_opt.cc @@ -78,7 +78,7 @@ struct FsmOpt bool signal_is_unused(RTLIL::SigSpec sig) { - RTLIL::SigBit bit = sig.to_single_sigbit(); + RTLIL::SigBit bit = sig.as_bit(); if (bit.wire == NULL || bit.wire->attributes.count("\\unused_bits") == 0) return false; diff --git a/passes/fsm/fsm_recode.cc b/passes/fsm/fsm_recode.cc index aa1e99be..a4b45295 100644 --- a/passes/fsm/fsm_recode.cc +++ b/passes/fsm/fsm_recode.cc @@ -85,7 +85,7 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs fsm_data.state_bits = fsm_data.state_table.size(); } else if (encoding == "binary") { - int new_num_state_bits = ceil(log2(fsm_data.state_table.size())); + int new_num_state_bits = ceil_log2(fsm_data.state_table.size()); if (fsm_data.state_bits == new_num_state_bits) { log(" existing encoding is already a packed binary encoding.\n"); return; diff --git a/passes/fsm/fsmdata.h b/passes/fsm/fsmdata.h index 1b98ccba..68222769 100644 --- a/passes/fsm/fsmdata.h +++ b/passes/fsm/fsmdata.h @@ -39,7 +39,7 @@ struct FsmData int state_num_log2 = 0; for (int i = state_table.size(); i > 0; i = i >> 1) state_num_log2++; - state_num_log2 = std::max(state_num_log2, 1); + state_num_log2 = max(state_num_log2, 1); cell->parameters["\\STATE_BITS"] = RTLIL::Const(state_bits); cell->parameters["\\STATE_NUM"] = RTLIL::Const(state_table.size()); diff --git a/passes/hierarchy/Makefile.inc b/passes/hierarchy/Makefile.inc index 99aa1e11..1fb669c1 100644 --- a/passes/hierarchy/Makefile.inc +++ b/passes/hierarchy/Makefile.inc @@ -1,4 +1,5 @@ OBJS += passes/hierarchy/hierarchy.o +OBJS += passes/hierarchy/singleton.o OBJS += passes/hierarchy/submod.o diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 598fe939..fcc30d17 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -66,7 +66,7 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, for (auto &conn : i2.second->connections()) { if (conn.first[0] != '$') portnames.insert(conn.first); - portwidths[conn.first] = std::max(portwidths[conn.first], conn.second.size()); + portwidths[conn.first] = max(portwidths[conn.first], conn.second.size()); } for (auto ¶ : i2.second->parameters) parameters.insert(para.first); @@ -84,8 +84,8 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, for (auto &decl : portdecls) if (decl.index > 0) { - portwidths[decl.portname] = std::max(portwidths[decl.portname], 1); - portwidths[decl.portname] = std::max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]); + portwidths[decl.portname] = max(portwidths[decl.portname], 1); + portwidths[decl.portname] = max(portwidths[decl.portname], portwidths[stringf("$%d", decl.index)]); log(" port %d: %s [%d:0] %s\n", decl.index, decl.input ? decl.output ? "inout" : "input" : "output", portwidths[decl.portname]-1, RTLIL::id2cstr(decl.portname)); if (indices.count(decl.index) > ports.size()) log_error("Port index (%d) exceeds number of found ports (%d).\n", decl.index, int(ports.size())); @@ -106,7 +106,7 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes, log_assert(!indices.empty()); indices.erase(d.index); ports[d.index-1] = d; - portwidths[d.portname] = std::max(portwidths[d.portname], 1); + portwidths[d.portname] = max(portwidths[d.portname], 1); log(" port %d: %s [%d:0] %s\n", d.index, d.input ? d.output ? "inout" : "input" : "output", portwidths[d.portname]-1, RTLIL::id2cstr(d.portname)); goto found_matching_decl; } @@ -327,7 +327,7 @@ int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db) db[module] = 0; for (auto cell : module->cells()) if (design->module(cell->type)) - db[module] = std::max(db[module], find_top_mod_score(design, design->module(cell->type), db) + 1); + db[module] = max(db[module], find_top_mod_score(design, design->module(cell->type), db) + 1); } return db.at(module); } diff --git a/passes/hierarchy/singleton.cc b/passes/hierarchy/singleton.cc new file mode 100644 index 00000000..5715c0eb --- /dev/null +++ b/passes/hierarchy/singleton.cc @@ -0,0 +1,101 @@ +/* + * 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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct SingletonPass : public Pass { + SingletonPass() : Pass("singleton", "create singleton modules") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" singleton [selection]\n"); + log("\n"); + log("By default, a module that is instantiated by several other modules is only\n"); + log("kept once in the design. This preserves the original modularity of the design\n"); + log("and reduces the overall size of the design in memory. But it prevents certain\n"); + log("optimizations and other operations on the design. This pass creates singleton\n"); + log("modules for all selected cells. The created modules are marked with the\n"); + log("'singleton' attribute.\n"); + log("\n"); + log("This commands only operates on modules that by themself have the 'singleton'\n"); + log("attribute set (the 'top' module is a singleton implicitly).\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header("Executing SINGLETON pass (creating singleton modules).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-check") { + // flag_check = true; + // continue; + // } + } + extra_args(args, argidx, design); + + bool did_something = true; + int singleton_cnt = 0; + + while (did_something) + { + did_something = false; + + for (auto module : design->selected_modules()) + { + if (!module->get_bool_attribute("\\singleton") && !module->get_bool_attribute("\\top")) + continue; + + for (auto cell : module->selected_cells()) + { + auto tmod = design->module(cell->type); + + if (tmod == nullptr) + continue; + + if (tmod->get_bool_attribute("\\blackbox")) + continue; + + if (tmod->get_bool_attribute("\\singleton")) + continue; + + cell->type = module->name.str() + "." + log_id(cell->name); + log("Creating singleton '%s'.\n", log_id(cell->type)); + + auto smod = tmod->clone(); + smod->name = cell->type; + smod->set_bool_attribute("\\singleton"); + design->add(smod); + + did_something = true; + singleton_cnt++; + } + } + } + + log("Created %d singleton modules.\n", singleton_cnt); + } +} SingletonPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/hierarchy/submod.cc b/passes/hierarchy/submod.cc index be46d882..d4e8c96c 100644 --- a/passes/hierarchy/submod.cc +++ b/passes/hierarchy/submod.cc @@ -32,6 +32,8 @@ struct SubmodWorker CellTypes ct; RTLIL::Design *design; RTLIL::Module *module; + + bool copy_mode; std::string opt_name; struct SubModule @@ -177,21 +179,25 @@ struct SubmodWorker bit.wire = wire_flags[bit.wire].new_wire; } log(" cell %s (%s)\n", new_cell->name.c_str(), new_cell->type.c_str()); - module->remove(cell); + if (!copy_mode) + module->remove(cell); } submod.cells.clear(); - RTLIL::Cell *new_cell = module->addCell(submod.full_name, submod.full_name); - for (auto &it : wire_flags) - { - RTLIL::Wire *old_wire = it.first; - RTLIL::Wire *new_wire = it.second.new_wire; - if (new_wire->port_id > 0) - new_cell->setPort(new_wire->name, RTLIL::SigSpec(old_wire)); + if (!copy_mode) { + RTLIL::Cell *new_cell = module->addCell(submod.full_name, submod.full_name); + for (auto &it : wire_flags) + { + RTLIL::Wire *old_wire = it.first; + RTLIL::Wire *new_wire = it.second.new_wire; + if (new_wire->port_id > 0) + new_cell->setPort(new_wire->name, RTLIL::SigSpec(old_wire)); + } } } - SubmodWorker(RTLIL::Design *design, RTLIL::Module *module, std::string opt_name = std::string()) : design(design), module(module), opt_name(opt_name) + SubmodWorker(RTLIL::Design *design, RTLIL::Module *module, bool copy_mode = false, std::string opt_name = std::string()) : + design(design), module(module), copy_mode(copy_mode), opt_name(opt_name) { if (!design->selected_whole_module(module->name) && opt_name.empty()) return; @@ -266,7 +272,7 @@ struct SubmodPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" submod [selection]\n"); + log(" submod [-copy] [selection]\n"); log("\n"); log("This pass identifies all cells with the 'submod' attribute and moves them to\n"); log("a newly created module. The value of the attribute is used as name for the\n"); @@ -279,12 +285,16 @@ struct SubmodPass : public Pass { log("or memories.\n"); log("\n"); log("\n"); - log(" submod -name <name> [selection]\n"); + log(" submod -name <name> [-copy] [selection]\n"); log("\n"); log("As above, but don't use the 'submod' attribute but instead use the selection.\n"); log("Only objects from one module might be selected. The value of the -name option\n"); log("is used as the value of the 'submod' attribute above.\n"); log("\n"); + log("By default the cells are 'moved' from the source module and the source module\n"); + log("will use an instance of the new module after this command is finished. Call\n"); + log("with -copy to not modify the source module.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { @@ -292,6 +302,7 @@ struct SubmodPass : public Pass { log_push(); std::string opt_name; + bool copy_mode = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -299,6 +310,10 @@ struct SubmodPass : public Pass { opt_name = args[++argidx]; continue; } + if (args[argidx] == "-copy") { + copy_mode = true; + continue; + } break; } extra_args(args, argidx, design); @@ -319,7 +334,7 @@ struct SubmodPass : public Pass { queued_modules.push_back(mod_it.first); for (auto &modname : queued_modules) if (design->modules_.count(modname) != 0) { - SubmodWorker worker(design, design->modules_[modname]); + SubmodWorker worker(design, design->modules_[modname], copy_mode); handled_modules.insert(modname); did_something = true; } @@ -342,7 +357,7 @@ struct SubmodPass : public Pass { else { Pass::call_on_module(design, module, "opt_clean"); log_header("Continuing SUBMOD pass.\n"); - SubmodWorker worker(design, module, opt_name); + SubmodWorker worker(design, module, copy_mode, opt_name); } } diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc index b371578d..f2d9b584 100644 --- a/passes/memory/memory_bram.cc +++ b/passes/memory/memory_bram.cc @@ -387,9 +387,9 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, if (pi.clkpol > 1) clkpol_wr_ports.insert(pi.clkpol); } - clocks_max = std::max(clocks_max, pi.clocks); - clkpol_max = std::max(clkpol_max, pi.clkpol); - transp_max = std::max(transp_max, pi.transp); + clocks_max = max(clocks_max, pi.clocks); + clkpol_max = max(clkpol_max, pi.clkpol); + transp_max = max(transp_max, pi.transp); } log(" Mapping to bram type %s (variant %d):\n", log_id(bram.name), bram.variant); @@ -977,7 +977,7 @@ void handle_cell(Cell *cell, const rules_t &rules) log("\n"); pool<pair<IdString, int>> failed_brams; - dict<pair<int, int>, std::tuple<int, int, int>> best_rule_cache; + dict<pair<int, int>, tuple<int, int, int>> best_rule_cache; for (int i = 0; i < GetSize(rules.matches); i++) { @@ -1078,7 +1078,7 @@ void handle_cell(Cell *cell, const rules_t &rules) } log(" Storing for later selection.\n"); - best_rule_cache[pair<int, int>(i, vi)] = std::tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]); + best_rule_cache[pair<int, int>(i, vi)] = tuple<int, int, int>(match_properties["efficiency"], -match_properties["cells"], -match_properties["acells"]); next_match_rule: if (or_next_if_better || best_rule_cache.empty()) diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc index abd4b124..5c0acb3e 100644 --- a/passes/memory/memory_collect.cc +++ b/passes/memory/memory_collect.cc @@ -64,7 +64,7 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory) for (auto &cell_it : module->cells_) { Cell *cell = cell_it.second; if (cell->type.in("$memrd", "$memwr", "$meminit") && memory->name == cell->parameters["\\MEMID"].decode_string()) { - addr_bits = std::max(addr_bits, cell->getParam("\\ABITS").as_int()); + addr_bits = max(addr_bits, cell->getParam("\\ABITS").as_int()); memcells.push_back(cell); } } diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc index 3373369f..beb2016a 100644 --- a/passes/memory/memory_dff.cc +++ b/passes/memory/memory_dff.cc @@ -32,6 +32,7 @@ struct MemoryDffWorker dict<SigBit, SigBit> invbits; dict<SigBit, int> sigbit_users_count; dict<SigSpec, Cell*> mux_cells_a, mux_cells_b; + pool<Cell*> forward_merged_dffs, candidate_dffs; MemoryDffWorker(Module *module) : module(module), sigmap(module) { } @@ -46,6 +47,9 @@ struct MemoryDffWorker for (auto cell : dff_cells) { + if (after && forward_merged_dffs.count(cell)) + continue; + SigSpec this_clk = cell->getPort("\\CLK"); bool this_clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool(); @@ -71,6 +75,7 @@ struct MemoryDffWorker bit = d; clk = this_clk; clk_polarity = this_clk_polarity; + candidate_dffs.insert(cell); goto replaced_this_bit; } @@ -87,6 +92,7 @@ struct MemoryDffWorker RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx); bool clk_polarity = 0; + candidate_dffs.clear(); RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR"); if (!find_sig_before_dff(sig_addr, clk, clk_polarity)) { @@ -106,13 +112,18 @@ struct MemoryDffWorker return; } - if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) { + if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) + { + for (auto cell : candidate_dffs) + forward_merged_dffs.insert(cell); + cell->setPort("\\CLK", clk); cell->setPort("\\ADDR", sig_addr); cell->setPort("\\DATA", sig_data); cell->setPort("\\EN", sig_en); cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); + log("merged $dff to cell.\n"); return; } @@ -153,7 +164,7 @@ struct MemoryDffWorker if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data)) { - bool enable_invert = mux_cells_a.count(sig_data); + bool enable_invert = mux_cells_a.count(sig_data) != 0; Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data); SigSpec check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A")); diff --git a/passes/memory/memory_share.cc b/passes/memory/memory_share.cc index b8f27025..3a6fd0b4 100644 --- a/passes/memory/memory_share.cc +++ b/passes/memory/memory_share.cc @@ -210,7 +210,7 @@ struct MemoryShareWorker if (non_feedback_nets.count(sig_data[i])) goto not_pure_feedback_port; - async_rd_bits[sig_addr].resize(std::max(async_rd_bits.size(), sig_data.size())); + async_rd_bits[sig_addr].resize(max(async_rd_bits.size(), sig_data.size())); for (int i = 0; i < int(sig_data.size()); i++) async_rd_bits[sig_addr][i].insert(sig_data[i]); diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 49615d9d..175e8e11 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -177,7 +177,7 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPoo if (attrs1 != attrs2) return attrs2 > attrs1; - return w2->name < w1->name; + return strcmp(w2->name.c_str(), w1->name.c_str()) < 0; } bool check_public_name(RTLIL::IdString id) diff --git a/passes/opt/opt_const.cc b/passes/opt/opt_const.cc index 85e4c4c1..0fcacf0a 100644 --- a/passes/opt/opt_const.cc +++ b/passes/opt/opt_const.cc @@ -589,7 +589,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec b = cell->getPort("\\B"); if (cell->parameters["\\A_WIDTH"].as_int() != cell->parameters["\\B_WIDTH"].as_int()) { - int width = std::max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int()); + int width = max(cell->parameters["\\A_WIDTH"].as_int(), cell->parameters["\\B_WIDTH"].as_int()); a.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool()); b.extend_u0(width, cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool()); } diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 15d59202..905a0162 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -136,7 +136,7 @@ struct OptMuxtreeWorker } } for (auto wire : module->wires()) { - if (wire->port_output) + if (wire->port_output || wire->get_bool_attribute("\\keep")) for (int idx : sig2bits(RTLIL::SigSpec(wire))) bit2info[idx].seen_non_mux = true; } diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 9dd0dc0a..1d9b1006 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -113,8 +113,8 @@ struct ShareWorker static int bits_macc_port(const Macc::port_t &p, int width) { if (GetSize(p.in_a) == 0 || GetSize(p.in_b) == 0) - return std::min(std::max(GetSize(p.in_a), GetSize(p.in_b)), width); - return std::min(GetSize(p.in_a), width) * std::min(GetSize(p.in_b), width) / 2; + return min(max(GetSize(p.in_a), GetSize(p.in_b)), width); + return min(GetSize(p.in_a), width) * min(GetSize(p.in_b), width) / 2; } static int bits_macc(const Macc &m, int width) @@ -224,13 +224,13 @@ struct ShareWorker supermacc->ports.push_back(p); } - int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * std::max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1); + int score = 1000 + abs(GetSize(p1.in_a) - GetSize(p2.in_a)) * max(abs(GetSize(p1.in_b) - GetSize(p2.in_b)), 1); - for (int i = 0; i < std::min(GetSize(p1.in_a), GetSize(p2.in_a)); i++) + for (int i = 0; i < min(GetSize(p1.in_a), GetSize(p2.in_a)); i++) if (p1.in_a[i] == p2.in_a[i] && score > 0) score--; - for (int i = 0; i < std::min(GetSize(p1.in_b), GetSize(p2.in_b)); i++) + for (int i = 0; i < min(GetSize(p1.in_b), GetSize(p2.in_b)); i++) if (p1.in_b[i] == p2.in_b[i] && score > 0) score--; @@ -243,7 +243,7 @@ struct ShareWorker Macc m1(c1), m2(c2), supermacc; int w1 = GetSize(c1->getPort("\\Y")), w2 = GetSize(c2->getPort("\\Y")); - int width = std::max(w1, w2); + int width = max(w1, w2); m1.optimize(w1); m2.optimize(w2); @@ -419,8 +419,8 @@ struct ShareWorker int a2_width = c2->parameters.at("\\A_WIDTH").as_int(); int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); - if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false; - if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false; + if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false; } return true; @@ -438,9 +438,9 @@ struct ShareWorker int b2_width = c2->parameters.at("\\B_WIDTH").as_int(); int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); - if (std::max(a1_width, a2_width) > 2 * std::min(a1_width, a2_width)) return false; - if (std::max(b1_width, b2_width) > 2 * std::min(b1_width, b2_width)) return false; - if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + if (max(a1_width, a2_width) > 2 * min(a1_width, a2_width)) return false; + if (max(b1_width, b2_width) > 2 * min(b1_width, b2_width)) return false; + if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false; } return true; @@ -458,15 +458,15 @@ struct ShareWorker int b2_width = c2->parameters.at("\\B_WIDTH").as_int(); int y2_width = c2->parameters.at("\\Y_WIDTH").as_int(); - int min1_width = std::min(a1_width, b1_width); - int max1_width = std::max(a1_width, b1_width); + int min1_width = min(a1_width, b1_width); + int max1_width = max(a1_width, b1_width); - int min2_width = std::min(a2_width, b2_width); - int max2_width = std::max(a2_width, b2_width); + int min2_width = min(a2_width, b2_width); + int max2_width = max(a2_width, b2_width); - if (std::max(min1_width, min2_width) > 2 * std::min(min1_width, min2_width)) return false; - if (std::max(max1_width, max2_width) > 2 * std::min(max1_width, max2_width)) return false; - if (std::max(y1_width, y2_width) > 2 * std::min(y1_width, y2_width)) return false; + if (max(min1_width, min2_width) > 2 * min(min1_width, min2_width)) return false; + if (max(max1_width, max2_width) > 2 * min(max1_width, max2_width)) return false; + if (max(y1_width, y2_width) > 2 * min(y1_width, y2_width)) return false; } return true; @@ -475,7 +475,7 @@ struct ShareWorker if (c1->type == "$macc") { if (!config.opt_aggressive) - if (share_macc(c1, c2) > 2 * std::min(bits_macc(c1), bits_macc(c2))) return false; + if (share_macc(c1, c2) > 2 * min(bits_macc(c1), bits_macc(c2))) return false; return true; } @@ -532,8 +532,8 @@ struct ShareWorker RTLIL::SigSpec a2 = c2->getPort("\\A"); RTLIL::SigSpec y2 = c2->getPort("\\Y"); - int a_width = std::max(a1.size(), a2.size()); - int y_width = std::max(y1.size(), y2.size()); + int a_width = max(a1.size(), a2.size()); + int y_width = max(y1.size(), y2.size()); a1.extend_u0(a_width, a_signed); a2.extend_u0(a_width, a_signed); @@ -563,11 +563,11 @@ struct ShareWorker if (config.generic_cbin_ops.count(c1->type)) { - int score_unflipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) + - std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()); + int score_unflipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()) + + max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()); - int score_flipped = std::max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) + - std::max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()); + int score_flipped = max(c1->parameters.at("\\A_WIDTH").as_int(), c2->parameters.at("\\B_WIDTH").as_int()) + + max(c1->parameters.at("\\B_WIDTH").as_int(), c2->parameters.at("\\A_WIDTH").as_int()); if (score_flipped < score_unflipped) { @@ -630,13 +630,13 @@ struct ShareWorker RTLIL::SigSpec b2 = c2->getPort("\\B"); RTLIL::SigSpec y2 = c2->getPort("\\Y"); - int a_width = std::max(a1.size(), a2.size()); - int b_width = std::max(b1.size(), b2.size()); - int y_width = std::max(y1.size(), y2.size()); + int a_width = max(a1.size(), a2.size()); + int b_width = max(b1.size(), b2.size()); + int y_width = max(y1.size(), y2.size()); if (c1->type == "$shr" && a_signed) { - a_width = std::max(y_width, a_width); + a_width = max(y_width, a_width); if (a1.size() < y1.size()) a1.extend_u0(y1.size(), true); if (a2.size() < y2.size()) a2.extend_u0(y2.size(), true); diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index e551443f..4f08da67 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -51,6 +51,7 @@ struct WreduceWorker std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells; std::set<SigBit> work_queue_bits; + pool<SigBit> keep_bits; WreduceWorker(WreduceConfig *config, Module *module) : config(config), module(module), mi(module) { } @@ -65,10 +66,13 @@ struct WreduceWorker SigSpec sig_y = mi.sigmap(cell->getPort("\\Y")); std::vector<SigBit> bits_removed; + if (sig_y.has_const()) + return; + for (int i = GetSize(sig_y)-1; i >= 0; i--) { auto info = mi.query(sig_y[i]); - if (!info->is_output && GetSize(info->ports) <= 1) { + if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_y[i]))) { bits_removed.push_back(Sx); continue; } @@ -172,6 +176,11 @@ struct WreduceWorker if (cell->type.in("$mux", "$pmux")) return run_cell_mux(cell); + SigSpec sig = mi.sigmap(cell->getPort("\\Y")); + + if (sig.has_const()) + return; + // Reduce size of ports A and B based on constant input bits and size of output port @@ -179,8 +188,8 @@ struct WreduceWorker int max_port_b_size = cell->hasPort("\\B") ? GetSize(cell->getPort("\\B")) : -1; if (cell->type.in("$not", "$pos", "$neg", "$and", "$or", "$xor", "$add", "$sub")) { - max_port_a_size = std::min(max_port_a_size, GetSize(cell->getPort("\\Y"))); - max_port_b_size = std::min(max_port_b_size, GetSize(cell->getPort("\\Y"))); + max_port_a_size = min(max_port_a_size, GetSize(sig)); + max_port_b_size = min(max_port_b_size, GetSize(sig)); } bool port_a_signed = false; @@ -192,10 +201,33 @@ struct WreduceWorker if (max_port_b_size >= 0) run_reduce_inport(cell, 'B', max_port_b_size, port_b_signed, did_something); + if (cell->hasPort("\\A") && cell->hasPort("\\B") && port_a_signed && port_b_signed) { + SigSpec sig_a = mi.sigmap(cell->getPort("\\A")), sig_b = mi.sigmap(cell->getPort("\\B")); + if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0 && + GetSize(sig_b) > 0 && sig_b[GetSize(sig_b)-1] == State::S0) { + log("Converting cell %s.%s (%s) from signed to unsigned.\n", + log_id(module), log_id(cell), log_id(cell->type)); + cell->setParam("\\A_SIGNED", 0); + cell->setParam("\\B_SIGNED", 0); + port_a_signed = false; + port_b_signed = false; + did_something = true; + } + } + + if (cell->hasPort("\\A") && !cell->hasPort("\\B") && port_a_signed) { + SigSpec sig_a = mi.sigmap(cell->getPort("\\A")); + if (GetSize(sig_a) > 0 && sig_a[GetSize(sig_a)-1] == State::S0) { + log("Converting cell %s.%s (%s) from signed to unsigned.\n", + log_id(module), log_id(cell), log_id(cell->type)); + cell->setParam("\\A_SIGNED", 0); + port_a_signed = false; + did_something = true; + } + } - // Reduce size of port Y based on sizes for A and B and unused bits in Y - SigSpec sig = mi.sigmap(cell->getPort("\\Y")); + // Reduce size of port Y based on sizes for A and B and unused bits in Y int bits_removed = 0; if (port_a_signed && cell->type == "$shr") { @@ -221,7 +253,7 @@ struct WreduceWorker if (cell->hasPort("\\A")) a_size = GetSize(cell->getPort("\\A")); if (cell->hasPort("\\B")) b_size = GetSize(cell->getPort("\\B")); - int max_y_size = std::max(a_size, b_size); + int max_y_size = max(a_size, b_size); if (cell->type == "$add") max_y_size++; @@ -265,6 +297,11 @@ struct WreduceWorker void run() { + for (auto w : module->wires()) + if (w->get_bool_attribute("\\keep")) + for (auto bit : mi.sigmap(w)) + keep_bits.insert(bit); + for (auto c : module->selected_cells()) work_queue_cells.insert(c); @@ -352,10 +389,12 @@ struct WreducePass : public Pass { "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", "$logic_not", "$logic_and", "$logic_or") && GetSize(c->getPort("\\Y")) > 1) { SigSpec sig = c->getPort("\\Y"); - c->setPort("\\Y", sig[0]); - c->setParam("\\Y_WIDTH", 1); - sig.remove(0); - module->connect(sig, Const(0, GetSize(sig))); + if (!sig.has_const()) { + c->setPort("\\Y", sig[0]); + c->setParam("\\Y_WIDTH", 1); + sig.remove(0); + module->connect(sig, Const(0, GetSize(sig))); + } } WreduceWorker worker(&config, module); diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 904d9211..943e8c56 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -27,35 +27,121 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -RTLIL::SigSpec find_any_lvalue(const RTLIL::CaseRule *cs) +struct SigSnippets { - for (auto &action : cs->actions) { - if (action.first.size()) - return action.first; - } + idict<SigSpec> sigidx; + dict<SigBit, int> bit2snippet; + pool<int> snippets; - for (auto sw : cs->switches) - for (auto cs2 : sw->cases) { - RTLIL::SigSpec sig = find_any_lvalue(cs2); - if (sig.size()) - return sig; + void insert(SigSpec sig) + { + if (sig.empty()) + return; + + int key = sigidx(sig); + if (snippets.count(key)) + return; + + SigSpec new_sig; + + for (int i = 0; i < GetSize(sig); i++) + { + int other_key = bit2snippet.at(sig[i], -1); + + if (other_key < 0) { + new_sig.append(sig[i]); + continue; + } + + if (!new_sig.empty()) { + int new_key = sigidx(new_sig); + snippets.insert(new_key); + for (auto bit : new_sig) + bit2snippet[bit] = new_key; + new_sig = SigSpec(); + } + + SigSpec other_sig = sigidx[other_key]; + int k = 0, n = 1; + + while (other_sig[k] != sig[i]) { + k++; + log_assert(k < GetSize(other_sig)); + } + + while (i+n < GetSize(sig) && k+n < GetSize(other_sig) && sig[i+n] == other_sig[k+n]) + n++; + + SigSpec sig1 = other_sig.extract(0, k); + SigSpec sig2 = other_sig.extract(k, n); + SigSpec sig3 = other_sig.extract(k+n, GetSize(other_sig)-k-n); + + for (auto bit : other_sig) + bit2snippet.erase(bit); + snippets.erase(other_key); + + insert(sig1); + insert(sig2); + insert(sig3); + + i += n-1; + } + + if (!new_sig.empty()) { + int new_key = sigidx(new_sig); + snippets.insert(new_key); + for (auto bit : new_sig) + bit2snippet[bit] = new_key; + } } - return RTLIL::SigSpec(); -} + void insert(const RTLIL::CaseRule *cs) + { + for (auto &action : cs->actions) + insert(action.first); -void extract_core_signal(const RTLIL::CaseRule *cs, RTLIL::SigSpec &sig) + for (auto sw : cs->switches) + for (auto cs2 : sw->cases) + insert(cs2); + } +}; + +struct SnippetSwCache { - for (auto &action : cs->actions) { - RTLIL::SigSpec lvalue = action.first.extract(sig); - if (lvalue.size()) - sig = lvalue; + dict<RTLIL::SwitchRule*, pool<int>, hash_ptr_ops> cache; + const SigSnippets *snippets; + int current_snippet; + + bool check(RTLIL::SwitchRule *sw) + { + return cache[sw].count(current_snippet) != 0; } - for (auto sw : cs->switches) - for (auto cs2 : sw->cases) - extract_core_signal(cs2, sig); -} + void insert(const RTLIL::CaseRule *cs, vector<RTLIL::SwitchRule*> &sw_stack) + { + for (auto &action : cs->actions) + for (auto bit : action.first) { + int sn = snippets->bit2snippet.at(bit, -1); + if (sn < 0) + continue; + for (auto sw : sw_stack) + cache[sw].insert(sn); + } + + for (auto sw : cs->switches) { + sw_stack.push_back(sw); + for (auto cs2 : sw->cases) + insert(cs2, sw_stack); + sw_stack.pop_back(); + } + } + + void insert(const RTLIL::CaseRule *cs) + { + vector<RTLIL::SwitchRule*> sw_stack; + insert(cs, sw_stack); + } +}; RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw) { @@ -179,7 +265,8 @@ void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::ve last_mux_cell->parameters["\\S_WIDTH"] = last_mux_cell->getPort("\\S").size(); } -RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval) +RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> &swpara, + RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval) { RTLIL::SigSpec result = defval; @@ -190,9 +277,37 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const for (auto sw : cs->switches) { + if (!swcache.check(sw)) + continue; + // detect groups of parallel cases std::vector<int> pgroups(sw->cases.size()); + bool is_simple_parallel_case = true; + if (!sw->get_bool_attribute("\\parallel_case")) { + if (!swpara.count(sw)) { + pool<Const> case_values; + for (size_t i = 0; i < sw->cases.size(); i++) { + RTLIL::CaseRule *cs2 = sw->cases[i]; + for (auto pat : cs2->compare) { + if (!pat.is_fully_def()) + goto not_simple_parallel_case; + Const cpat = pat.as_const(); + if (case_values.count(cpat)) + goto not_simple_parallel_case; + case_values.insert(cpat); + } + } + if (0) + not_simple_parallel_case: + is_simple_parallel_case = false; + swpara[sw] = is_simple_parallel_case; + } else { + is_simple_parallel_case = swpara.at(sw); + } + } + + if (!is_simple_parallel_case) { BitPatternPool pool(sw->signal.size()); bool extra_group_for_next_case = false; for (size_t i = 0; i < sw->cases.size(); i++) { @@ -225,7 +340,7 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const for (size_t i = 0; i < sw->cases.size(); i++) { int case_idx = sw->cases.size() - i - 1; RTLIL::CaseRule *cs2 = sw->cases[case_idx]; - RTLIL::SigSpec value = signal_to_mux_tree(mod, cs2, sig, initial_val); + RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val); if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1]) append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw); else @@ -238,24 +353,26 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc) { - bool first = true; - while (1) - { - RTLIL::SigSpec sig = find_any_lvalue(&proc->root_case); + log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str()); - if (sig.size() == 0) - break; + SigSnippets sigsnip; + sigsnip.insert(&proc->root_case); - if (first) { - log("Creating decoders for process `%s.%s'.\n", mod->name.c_str(), proc->name.c_str()); - first = false; - } + SnippetSwCache swcache; + swcache.snippets = &sigsnip; + swcache.insert(&proc->root_case); + + dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> swpara; - extract_core_signal(&proc->root_case, sig); + int cnt = 0; + for (int idx : sigsnip.snippets) + { + swcache.current_snippet = idx; + RTLIL::SigSpec sig = sigsnip.sigidx[idx]; - log(" creating decoder for signal `%s'.\n", log_signal(sig)); + log("%6d/%d: %s\n", ++cnt, GetSize(sigsnip.snippets), log_signal(sig)); - RTLIL::SigSpec value = signal_to_mux_tree(mod, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size())); + RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size())); mod->connect(RTLIL::SigSig(sig, value)); } } diff --git a/passes/sat/eval.cc b/passes/sat/eval.cc index 52266403..614a1bd3 100644 --- a/passes/sat/eval.cc +++ b/passes/sat/eval.cc @@ -568,7 +568,7 @@ struct EvalPass : public Pass { if (tab_column_width.size() < row.size()) tab_column_width.resize(row.size()); for (size_t i = 0; i < row.size(); i++) - tab_column_width[i] = std::max(tab_column_width[i], int(row[i].size())); + tab_column_width[i] = max(tab_column_width[i], int(row[i].size())); } log("\n"); diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc index ca784890..ebdf2ed5 100644 --- a/passes/sat/expose.cc +++ b/passes/sat/expose.cc @@ -116,7 +116,7 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De info.cell = it.second; if (info.cell->type == "$dff") { - info.bit_clk = sigmap(info.cell->getPort("\\CLK")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\CLK")).as_bit(); info.clk_polarity = info.cell->parameters.at("\\CLK_POLARITY").as_bool(); std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->getPort("\\D")).to_sigbit_vector(); std::vector<RTLIL::SigBit> sig_q = sigmap(info.cell->getPort("\\Q")).to_sigbit_vector(); @@ -128,8 +128,8 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De } if (info.cell->type == "$adff") { - info.bit_clk = sigmap(info.cell->getPort("\\CLK")).to_single_sigbit(); - info.bit_arst = sigmap(info.cell->getPort("\\ARST")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\CLK")).as_bit(); + info.bit_arst = sigmap(info.cell->getPort("\\ARST")).as_bit(); info.clk_polarity = info.cell->parameters.at("\\CLK_POLARITY").as_bool(); info.arst_polarity = info.cell->parameters.at("\\ARST_POLARITY").as_bool(); std::vector<RTLIL::SigBit> sig_d = sigmap(info.cell->getPort("\\D")).to_sigbit_vector(); @@ -144,21 +144,21 @@ void create_dff_dq_map(std::map<RTLIL::IdString, dff_map_info_t> &map, RTLIL::De } if (info.cell->type == "$_DFF_N_" || info.cell->type == "$_DFF_P_") { - info.bit_clk = sigmap(info.cell->getPort("\\C")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\C")).as_bit(); info.clk_polarity = info.cell->type == "$_DFF_P_"; - info.bit_d = sigmap(info.cell->getPort("\\D")).to_single_sigbit(); - bit_info[sigmap(info.cell->getPort("\\Q")).to_single_sigbit()] = info; + info.bit_d = sigmap(info.cell->getPort("\\D")).as_bit(); + bit_info[sigmap(info.cell->getPort("\\Q")).as_bit()] = info; continue; } if (info.cell->type.size() == 10 && info.cell->type.substr(0, 6) == "$_DFF_") { - info.bit_clk = sigmap(info.cell->getPort("\\C")).to_single_sigbit(); - info.bit_arst = sigmap(info.cell->getPort("\\R")).to_single_sigbit(); + info.bit_clk = sigmap(info.cell->getPort("\\C")).as_bit(); + info.bit_arst = sigmap(info.cell->getPort("\\R")).as_bit(); info.clk_polarity = info.cell->type[6] == 'P'; info.arst_polarity = info.cell->type[7] == 'P'; info.arst_value = info.cell->type[0] == '1' ? RTLIL::State::S1 : RTLIL::State::S0; - info.bit_d = sigmap(info.cell->getPort("\\D")).to_single_sigbit(); - bit_info[sigmap(info.cell->getPort("\\Q")).to_single_sigbit()] = info; + info.bit_d = sigmap(info.cell->getPort("\\D")).as_bit(); + bit_info[sigmap(info.cell->getPort("\\Q")).as_bit()] = info; continue; } } diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc index e0d11243..373b8048 100644 --- a/passes/sat/freduce.cc +++ b/passes/sat/freduce.cc @@ -265,7 +265,7 @@ struct PerformReduction } int max_child_depth = 0; for (auto &bit : drv.second) - max_child_depth = std::max(register_cone_worker(celldone, sigdepth, bit), max_child_depth); + max_child_depth = max(register_cone_worker(celldone, sigdepth, bit), max_child_depth); sigdepth[out] = max_child_depth + 1; } else { pi_bits.push_back(out); diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index e72eddf3..2e9c6d2f 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -606,8 +606,8 @@ struct SatHelper int maxModelWidth = 10; for (auto &info : modelInfo) { - maxModelName = std::max(maxModelName, int(info.description.size())); - maxModelWidth = std::max(maxModelWidth, info.width); + maxModelName = max(maxModelName, int(info.description.size())); + maxModelWidth = max(maxModelWidth, info.width); } log("\n"); @@ -781,9 +781,9 @@ struct SatHelper wavedata[info.description].first = info.width; wavedata[info.description].second[info.timestep] = value; - mintime = std::min(mintime, info.timestep); - maxtime = std::max(maxtime, info.timestep); - maxwidth = std::max(maxwidth, info.width); + mintime = min(mintime, info.timestep); + maxtime = max(maxtime, info.timestep); + maxwidth = max(maxwidth, info.width); } fprintf(f, "{ \"signal\": ["); @@ -1116,7 +1116,7 @@ struct SatPass : public Pass { continue; } if (args[argidx] == "-stepsize" && argidx+1 < args.size()) { - stepsize = std::max(1, atoi(args[++argidx].c_str())); + stepsize = max(1, atoi(args[++argidx].c_str())); continue; } if (args[argidx] == "-ignore_div_by_zero") { diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 674cb77c..8112516c 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -22,6 +22,7 @@ OBJS += passes/techmap/aigmap.o OBJS += passes/techmap/tribuf.o OBJS += passes/techmap/lut2mux.o OBJS += passes/techmap/nlutmap.o +OBJS += passes/techmap/dffsr2dff.o endif GENFILES += passes/techmap/techmap.inc @@ -42,6 +43,6 @@ EXTRA_OBJS += passes/techmap/filterlib.o yosys-filterlib$(EXE): passes/techmap/filterlib.o $(Q) mkdir -p $(dir $@) - $(P) $(CXX) -o yosys-filterlib$(EXE) $(LDFLAGS) $^ $(LDLIBS) + $(P) $(LD) -o yosys-filterlib$(EXE) $(LDFLAGS) $^ $(LDLIBS) endif diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 64586927..7da26602 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -100,6 +100,7 @@ SigMap assign_map; RTLIL::Module *module; std::vector<gate_t> signal_list; std::map<RTLIL::SigBit, int> signal_map; +pool<std::string> enabled_gates; bool clk_polarity, en_polarity; RTLIL::SigSpec clk_sig, en_sig; @@ -591,7 +592,7 @@ struct abc_output_filter }; void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file, - std::string liberty_file, std::string constr_file, bool cleanup, int lut_mode, int lut_mode2, bool dff_mode, std::string clk_str, + std::string liberty_file, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str, bool keepff, std::string delay_target, bool fast_mode, const std::vector<RTLIL::Cell*> &cells, bool show_tempdir) { module = current_module; @@ -625,7 +626,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (!constr_file.empty()) abc_script += stringf("read_constr -v %s; ", constr_file.c_str()); } else - if (lut_mode) + if (!lut_costs.empty()) abc_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); else abc_script += stringf("read_library %s/stdcells.genlib; ", tempdir_name.c_str()); @@ -641,7 +642,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin abc_script += script_file[i]; } else abc_script += stringf("source %s", script_file.c_str()); - } else if (lut_mode) + } else if (!lut_costs.empty()) abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; else if (!liberty_file.empty()) abc_script += constr_file.empty() ? (fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR); @@ -837,17 +838,28 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin fprintf(f, "GATE ONE 1 Y=CONST1;\n"); fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_BUF_")); fprintf(f, "GATE NOT %d Y=!A; PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOT_")); - fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_")); - fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_")); - fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_")); - fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_")); - fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_")); - fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_")); - fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_")); - fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_")); - fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_")); - fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_")); - fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_")); + if (enabled_gates.empty() || enabled_gates.count("AND")) + fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_")); + if (enabled_gates.empty() || enabled_gates.count("NAND")) + fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_")); + if (enabled_gates.empty() || enabled_gates.count("OR")) + fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_")); + if (enabled_gates.empty() || enabled_gates.count("NOR")) + fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_")); + if (enabled_gates.empty() || enabled_gates.count("XOR")) + fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_")); + if (enabled_gates.empty() || enabled_gates.count("XNOR")) + fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_")); + if (enabled_gates.empty() || enabled_gates.count("AOI3")) + fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_")); + if (enabled_gates.empty() || enabled_gates.count("OAI3")) + fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_")); + if (enabled_gates.empty() || enabled_gates.count("AOI4")) + fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_")); + if (enabled_gates.empty() || enabled_gates.count("OAI4")) + fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_")); + if (enabled_gates.empty() || enabled_gates.count("MUX")) + fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_")); if (map_mux4) fprintf(f, "GATE MUX4 %d Y=(!S*!T*A)+(S*!T*B)+(!S*T*C)+(S*T*D); PIN * UNKNOWN 1 999 1 0 1 0\n", 2*get_cell_cost("$_MUX_")); if (map_mux8) @@ -856,15 +868,13 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*get_cell_cost("$_MUX_")); fclose(f); - if (lut_mode) { + if (!lut_costs.empty()) { buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str()); f = fopen(buffer.c_str(), "wt"); if (f == NULL) log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); - for (int i = 0; i < lut_mode; i++) - fprintf(f, "%d 1.00 1.00\n", i+1); - for (int i = lut_mode; i < lut_mode2; i++) - fprintf(f, "%d %d.00 1.00\n", i+1, 2 << (i - lut_mode)); + for (int i = 0; i < GetSize(lut_costs); i++) + fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i)); fclose(f); } @@ -882,7 +892,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (ifs.fail()) log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); - bool builtin_lib = liberty_file.empty() && script_file.empty() && !lut_mode; + bool builtin_lib = liberty_file.empty() && script_file.empty() && lut_costs.empty(); RTLIL::Design *mapped_design = new RTLIL::Design; parse_blif(mapped_design, ifs, builtin_lib ? "\\DFF" : "\\_dff_"); @@ -1226,10 +1236,19 @@ struct AbcPass : public Pass { log(" the area cost doubles with each additional input bit. the delay cost\n"); log(" is still constant for all lut widths.\n"); log("\n"); + log(" -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..\n"); + log(" generate netlist using luts. Use the specified costs for luts with 1,\n"); + log(" 2, 3, .. inputs.\n"); + log("\n"); // log(" -mux4, -mux8, -mux16\n"); // log(" try to extract 4-input, 8-input, and/or 16-input muxes\n"); // log(" (ignored when used with -liberty or -lut)\n"); // log("\n"); + log(" -g type1,type2,...\n"); + log(" Map the the specified list of gate types. Supported gates types are:\n"); + log(" AND, NAND, OR, NOR, XOR, XNOR, MUX, AOI3, OAI3, AOI4, OAI4.\n"); + log(" (The NOT gate is always added to this list automatically.)\n"); + log("\n"); log(" -dff\n"); log(" also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many\n"); log(" clock domains are automatically partitioned in clock domains and each\n"); @@ -1274,12 +1293,13 @@ struct AbcPass : public Pass { std::string script_file, liberty_file, constr_file, clk_str, delay_target; bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; bool show_tempdir = false; - int lut_mode = 0, lut_mode2 = 0; + vector<int> lut_costs; markgroups = false; map_mux4 = false; map_mux8 = false; map_mux16 = false; + enabled_gates.clear(); #ifdef _WIN32 if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe")) @@ -1323,6 +1343,7 @@ struct AbcPass : public Pass { if (arg == "-lut" && argidx+1 < args.size()) { string arg = args[++argidx]; size_t pos = arg.find_first_of(':'); + int lut_mode = 0, lut_mode2 = 0; if (pos != string::npos) { lut_mode = atoi(arg.substr(0, pos).c_str()); lut_mode2 = atoi(arg.substr(pos+1).c_str()); @@ -1330,6 +1351,27 @@ struct AbcPass : public Pass { lut_mode = atoi(arg.c_str()); lut_mode2 = lut_mode; } + lut_costs.clear(); + for (int i = 0; i < lut_mode; i++) + lut_costs.push_back(1); + for (int i = lut_mode; i < lut_mode2; i++) + lut_costs.push_back(2 << (i - lut_mode)); + continue; + } + if (arg == "-luts" && argidx+1 < args.size()) { + lut_costs.clear(); + for (auto &tok : split_tokens(args[++argidx], ",")) { + auto parts = split_tokens(tok, ":"); + if (GetSize(parts) == 0 && !lut_costs.empty()) + lut_costs.push_back(lut_costs.back()); + else if (GetSize(parts) == 1) + lut_costs.push_back(atoi(parts.at(0).c_str())); + else if (GetSize(parts) == 2) + while (GetSize(lut_costs) < atoi(parts.at(0).c_str())) + lut_costs.push_back(atoi(parts.at(1).c_str())); + else + log_cmd_error("Invalid -luts syntax.\n"); + } continue; } if (arg == "-mux4") { @@ -1344,6 +1386,25 @@ struct AbcPass : public Pass { map_mux16 = true; continue; } + if (arg == "-g" && argidx+1 < args.size()) { + for (auto g : split_tokens(args[++argidx], ",")) { + if (g == "AND") goto ok_gate; + if (g == "NAND") goto ok_gate; + if (g == "OR") goto ok_gate; + if (g == "NOR") goto ok_gate; + if (g == "XOR") goto ok_gate; + if (g == "XNOR") goto ok_gate; + if (g == "MUX") goto ok_gate; + if (g == "AOI3") goto ok_gate; + if (g == "OAI3") goto ok_gate; + if (g == "AOI4") goto ok_gate; + if (g == "OAI4") goto ok_gate; + cmd_error(args, argidx, stringf("Unsupported gate type: %s", g.c_str())); + ok_gate: + enabled_gates.insert(g); + } + continue; + } if (arg == "-fast") { fast_mode = true; continue; @@ -1377,7 +1438,7 @@ struct AbcPass : public Pass { } extra_args(args, argidx, design); - if (lut_mode != 0 && !liberty_file.empty()) + if (!lut_costs.empty() && !liberty_file.empty()) log_cmd_error("Got -lut and -liberty! This two options are exclusive.\n"); if (!constr_file.empty() && liberty_file.empty()) log_cmd_error("Got -constr but no -liberty!\n"); @@ -1386,7 +1447,7 @@ struct AbcPass : public Pass { if (mod->processes.size() > 0) log("Skipping module %s as it contains processes.\n", log_id(mod)); else if (!dff_mode || !clk_str.empty()) - abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_mode, lut_mode2, dff_mode, clk_str, keepff, delay_target, fast_mode, mod->selected_cells(), show_tempdir); + abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, delay_target, fast_mode, mod->selected_cells(), show_tempdir); else { assign_map.set(mod); @@ -1399,7 +1460,7 @@ struct AbcPass : public Pass { std::set<RTLIL::Cell*> expand_queue_up, next_expand_queue_up; std::set<RTLIL::Cell*> expand_queue_down, next_expand_queue_down; - typedef std::tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t; + typedef tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t; std::map<clkdomain_t, std::vector<RTLIL::Cell*>> assigned_cells; std::map<RTLIL::Cell*, clkdomain_t> assigned_cells_reverse; @@ -1530,7 +1591,7 @@ struct AbcPass : public Pass { clk_sig = assign_map(std::get<1>(it.first)); en_polarity = std::get<2>(it.first); en_sig = assign_map(std::get<3>(it.first)); - abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_mode, lut_mode2, + abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", keepff, delay_target, fast_mode, it.second, show_tempdir); assign_map.set(mod); } diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc index 90563c86..3c7ff4b9 100644 --- a/passes/techmap/alumacc.cc +++ b/passes/techmap/alumacc.cc @@ -40,7 +40,7 @@ struct AlumaccWorker { std::vector<RTLIL::Cell*> cells; RTLIL::SigSpec a, b, c, y; - std::vector<std::tuple<bool, bool, bool, bool, RTLIL::SigSpec>> cmp; + std::vector<tuple<bool, bool, bool, bool, RTLIL::SigSpec>> cmp; bool is_signed, invert_b; RTLIL::Cell *alu_cell; @@ -138,7 +138,7 @@ struct AlumaccWorker n->users = 0; for (auto bit : n->y) - n->users = std::max(n->users, bit_users.at(bit) - 1); + n->users = max(n->users, bit_users.at(bit) - 1); if (cell->type.in("$pos", "$neg")) { @@ -409,7 +409,7 @@ struct AlumaccWorker n->a = A; n->b = B; n->c = RTLIL::S1; - n->y = module->addWire(NEW_ID, std::max(GetSize(A), GetSize(B))); + n->y = module->addWire(NEW_ID, max(GetSize(A), GetSize(B))); n->is_signed = is_signed; n->invert_b = true; sig_alu[RTLIL::SigSig(A, B)].insert(n); diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc index 84770ff3..e0273f43 100644 --- a/passes/techmap/dffinit.cc +++ b/passes/techmap/dffinit.cc @@ -68,7 +68,7 @@ struct DffinitPass : public Pass { for (auto wire : module->selected_wires()) { if (wire->attributes.count("\\init")) { Const value = wire->attributes.at("\\init"); - for (int i = 0; i < std::min(GetSize(value), GetSize(wire)); i++) + for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) init_bits[sigmap(SigBit(wire, i))] = value[i]; } if (wire->port_output) @@ -100,7 +100,7 @@ struct DffinitPass : public Pass { for (int i = 0; i < GetSize(sig); i++) { if (init_bits.count(sig[i]) == 0) continue; - while (GetSize(value.bits) < i) + while (GetSize(value.bits) <= i) value.bits.push_back(State::S0); value.bits[i] = init_bits.at(sig[i]); cleanup_bits.insert(sig[i]); @@ -116,7 +116,7 @@ struct DffinitPass : public Pass { if (wire->attributes.count("\\init")) { Const &value = wire->attributes.at("\\init"); bool do_cleanup = true; - for (int i = 0; i < std::min(GetSize(value), GetSize(wire)); i++) { + for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) { SigBit bit = sigmap(SigBit(wire, i)); if (cleanup_bits.count(bit) || !used_bits.count(bit)) value[i] = State::Sx; diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index 693651bf..4d7a1a70 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -17,8 +17,8 @@ * */ -#include "kernel/register.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/sigtools.h" #include "libparse.h" #include <string.h> #include <errno.h> @@ -173,8 +173,12 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool std::string value = func->value; for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t")) value.erase(pos, 1); - if ((cell_next_pol == true && value == ff->args[0]) || (cell_next_pol == false && value == ff->args[1])) { - this_cell_ports[pin->args[0]] = 'Q'; + if (value == ff->args[0]) { + this_cell_ports[pin->args[0]] = cell_next_pol ? 'Q' : 'q'; + found_output = true; + } else + if (value == ff->args[1]) { + this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q'; found_output = true; } } @@ -274,8 +278,12 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo std::string value = func->value; for (size_t pos = value.find_first_of("\" \t"); pos != std::string::npos; pos = value.find_first_of("\" \t")) value.erase(pos, 1); - if ((cell_next_pol == true && value == ff->args[0]) || (cell_next_pol == false && value == ff->args[1])) { - this_cell_ports[pin->args[0]] = 'Q'; + if (value == ff->args[0]) { + this_cell_ports[pin->args[0]] = cell_next_pol ? 'Q' : 'q'; + found_output = true; + } else + if (value == ff->args[1]) { + this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q'; found_output = true; } } @@ -410,14 +418,42 @@ static void map_sr_to_arst(const char *from, const char *to) } } +static void map_adff_to_dff(const char *from, const char *to) +{ + if (!cell_mappings.count(from) || cell_mappings.count(to) > 0) + return; + + char from_clk_pol YS_ATTRIBUTE(unused) = from[6]; + char from_rst_pol = from[7]; + char to_clk_pol YS_ATTRIBUTE(unused) = to[6]; + + log_assert(from_clk_pol == to_clk_pol); + + log(" create mapping for %s from mapping for %s.\n", to, from); + cell_mappings[to].cell_name = cell_mappings[from].cell_name; + cell_mappings[to].ports = cell_mappings[from].ports; + + for (auto &it : cell_mappings[to].ports) { + if (it.second == 'S' || it.second == 'R') + it.second = from_rst_pol == 'P' ? '0' : '1'; + if (it.second == 's' || it.second == 'r') + it.second = from_rst_pol == 'P' ? '1' : '0'; + } +} + static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare_mode) { log("Mapping DFF cells in module `%s':\n", module->name.c_str()); + dict<SigBit, pool<Cell*>> notmap; + SigMap sigmap(module); + std::vector<RTLIL::Cell*> cell_list; for (auto &it : module->cells_) { if (design->selected(module, it.second) && cell_mappings.count(it.second->type) > 0) cell_list.push_back(it.second); + if (it.second->type == "$_NOT_") + notmap[sigmap(it.second->getPort("\\A"))].insert(it.second); } std::map<std::string, int> stats; @@ -431,6 +467,12 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare cell_mapping &cm = cell_mappings[cell_type]; RTLIL::Cell *new_cell = module->addCell(cell_name, prepare_mode ? cm.cell_name : "\\" + cm.cell_name); + bool has_q = false, has_qn = false; + for (auto &port : cm.ports) { + if (port.second == 'Q') has_q = true; + if (port.second == 'q') has_qn = true; + } + for (auto &port : cm.ports) { RTLIL::SigSpec sig; if ('A' <= port.second && port.second <= 'Z') { @@ -439,7 +481,14 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare if (port.second == 'q') { RTLIL::SigSpec old_sig = cell_connections[std::string("\\") + char(port.second - ('a' - 'A'))]; sig = module->addWire(NEW_ID, GetSize(old_sig)); - module->addNotGate(NEW_ID, sig, old_sig); + if (has_q && has_qn) { + for (auto &it : notmap[sigmap(old_sig)]) { + module->connect(it->getPort("\\Y"), sig); + it->setPort("\\Y", module->addWire(NEW_ID, GetSize(old_sig))); + } + } else { + module->addNotGate(NEW_ID, sig, old_sig); + } } else if ('a' <= port.second && port.second <= 'z') { sig = cell_connections[std::string("\\") + char(port.second - ('a' - 'A'))]; @@ -448,7 +497,9 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare if (port.second == '0' || port.second == '1') { sig = RTLIL::SigSpec(port.second == '0' ? 0 : 1, 1); } else - if (port.second != 0) + if (port.second == 0) { + sig = module->addWire(NEW_ID); + } else log_abort(); new_cell->setPort("\\" + port.first, sig); } @@ -564,6 +615,15 @@ struct DfflibmapPass : public Pass { map_sr_to_arst("$_DFFSR_PPP_", "$_DFF_PP0_"); map_sr_to_arst("$_DFFSR_PPP_", "$_DFF_PP1_"); + map_adff_to_dff("$_DFF_NN0_", "$_DFF_N_"); + map_adff_to_dff("$_DFF_NN1_", "$_DFF_N_"); + map_adff_to_dff("$_DFF_NP0_", "$_DFF_N_"); + map_adff_to_dff("$_DFF_NP1_", "$_DFF_N_"); + map_adff_to_dff("$_DFF_PN0_", "$_DFF_P_"); + map_adff_to_dff("$_DFF_PN1_", "$_DFF_P_"); + map_adff_to_dff("$_DFF_PP0_", "$_DFF_P_"); + map_adff_to_dff("$_DFF_PP1_", "$_DFF_P_"); + log(" final dff cell mappings:\n"); logmap_all(); diff --git a/passes/techmap/dffsr2dff.cc b/passes/techmap/dffsr2dff.cc new file mode 100644 index 00000000..8dcbb4ed --- /dev/null +++ b/passes/techmap/dffsr2dff.cc @@ -0,0 +1,213 @@ +/* + * 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 + +void dffsr_worker(SigMap &sigmap, Module *module, Cell *cell) +{ + if (cell->type == "$dffsr") + { + int width = cell->getParam("\\WIDTH").as_int(); + bool setpol = cell->getParam("\\SET_POLARITY").as_bool(); + bool clrpol = cell->getParam("\\CLR_POLARITY").as_bool(); + + SigBit setunused = setpol ? State::S0 : State::S1; + SigBit clrunused = clrpol ? State::S0 : State::S1; + + SigSpec setsig = sigmap(cell->getPort("\\SET")); + SigSpec clrsig = sigmap(cell->getPort("\\CLR")); + + Const reset_val; + SigSpec setctrl, clrctrl; + + for (int i = 0; i < width; i++) + { + SigBit setbit = setsig[i], clrbit = clrsig[i]; + + if (setbit == setunused) { + clrctrl.append(clrbit); + reset_val.bits.push_back(State::S0); + continue; + } + + if (clrbit == clrunused) { + setctrl.append(setbit); + reset_val.bits.push_back(State::S1); + continue; + } + + return; + } + + setctrl.sort_and_unify(); + clrctrl.sort_and_unify(); + + if (GetSize(setctrl) > 1 || GetSize(clrctrl) > 1) + return; + + if (GetSize(setctrl) == 0 && GetSize(clrctrl) == 0) + return; + + if (GetSize(setctrl) == 1 && GetSize(clrctrl) == 1) { + if (setpol != clrpol) + return; + if (setctrl != clrctrl) + return; + } + + log("Converting %s cell %s.%s to $adff.\n", log_id(cell->type), log_id(module), log_id(cell)); + + if (GetSize(setctrl) == 1) { + cell->setPort("\\ARST", setctrl); + cell->setParam("\\ARST_POLARITY", setpol); + } else { + cell->setPort("\\ARST", clrctrl); + cell->setParam("\\ARST_POLARITY", clrpol); + } + + cell->type = "$adff"; + cell->unsetPort("\\SET"); + cell->unsetPort("\\CLR"); + cell->setParam("\\ARST_VALUE", reset_val); + cell->unsetParam("\\SET_POLARITY"); + cell->unsetParam("\\CLR_POLARITY"); + + return; + } + + if (cell->type.in("$_DFFSR_NNN_", "$_DFFSR_NNP_", "$_DFFSR_NPN_", "$_DFFSR_NPP_", + "$_DFFSR_PNN_", "$_DFFSR_PNP_", "$_DFFSR_PPN_", "$_DFFSR_PPP_")) + { + char clkpol = cell->type.c_str()[8]; + char setpol = cell->type.c_str()[9]; + char clrpol = cell->type.c_str()[10]; + + SigBit setbit = sigmap(cell->getPort("\\S")); + SigBit clrbit = sigmap(cell->getPort("\\R")); + + SigBit setunused = setpol == 'P' ? State::S0 : State::S1; + SigBit clrunused = clrpol == 'P' ? State::S0 : State::S1; + + IdString oldtype = cell->type; + + if (setbit == setunused) { + cell->type = stringf("$_DFF_%c%c0_", clkpol, clrpol); + cell->unsetPort("\\S"); + goto converted_gate; + } + + if (clrbit == clrunused) { + cell->type = stringf("$_DFF_%c%c1_", clkpol, setpol); + cell->setPort("\\R", cell->getPort("\\S")); + cell->unsetPort("\\S"); + goto converted_gate; + } + + return; + + converted_gate: + log("Converting %s cell %s.%s to %s.\n", log_id(oldtype), log_id(module), log_id(cell), log_id(cell->type)); + return; + } +} + +void adff_worker(SigMap &sigmap, Module *module, Cell *cell) +{ + if (cell->type == "$adff") + { + bool rstpol = cell->getParam("\\ARST_POLARITY").as_bool(); + SigBit rstunused = rstpol ? State::S0 : State::S1; + SigSpec rstsig = sigmap(cell->getPort("\\ARST")); + + if (rstsig != rstunused) + return; + + log("Converting %s cell %s.%s to $dff.\n", log_id(cell->type), log_id(module), log_id(cell)); + + cell->type = "$dff"; + cell->unsetPort("\\ARST"); + cell->unsetParam("\\ARST_VALUE"); + cell->unsetParam("\\ARST_POLARITY"); + + return; + } + + if (cell->type.in("$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_", + "$_DFF_PN0_", "$_DFF_PN1_", "$_DFF_PP0_", "$_DFF_PP1_")) + { + char clkpol = cell->type.c_str()[6]; + char rstpol = cell->type.c_str()[7]; + + SigBit rstbit = sigmap(cell->getPort("\\R")); + SigBit rstunused = rstpol == 'P' ? State::S0 : State::S1; + + if (rstbit != rstunused) + return; + + IdString newtype = stringf("$_DFF_%c_", clkpol); + log("Converting %s cell %s.%s to %s.\n", log_id(cell->type), log_id(module), log_id(cell), log_id(newtype)); + + cell->type = newtype; + cell->unsetPort("\\R"); + + return; + } +} + +struct Dffsr2dffPass : public Pass { + Dffsr2dffPass() : Pass("dffsr2dff", "convert DFFSR cells to simpler FF cell types") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" dffsr2dff [options] [selection]\n"); + log("\n"); + log("This pass converts DFFSR cells ($dffsr, $_DFFSR_???_) and ADFF cells ($adff,\n"); + log("$_DFF_???_) to simpler FF cell types when any of the set/reset inputs is unused.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header("Executing DFFSR2DFF pass (mapping DFFSR cells to simpler FFs).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-v") { + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) { + SigMap sigmap(module); + for (auto cell : module->selected_cells()) { + dffsr_worker(sigmap, module, cell); + adff_worker(sigmap, module, cell); + } + } + } +} Dffsr2dffPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/extract.cc b/passes/techmap/extract.cc index 68a7fc1f..fc73177c 100644 --- a/passes/techmap/extract.cc +++ b/passes/techmap/extract.cc @@ -130,7 +130,7 @@ public: RTLIL::SigSpec needleSig = conn.second; RTLIL::SigSpec haystackSig = haystackCell->getPort(portMapping.at(conn.first.str())); - for (int i = 0; i < std::min(needleSig.size(), haystackSig.size()); i++) { + for (int i = 0; i < min(needleSig.size(), haystackSig.size()); i++) { RTLIL::Wire *needleWire = needleSig[i].wire, *haystackWire = haystackSig[i].wire; if (needleWire != lastNeedleWire || haystackWire != lastHaystackWire) if (!compareAttributes(wire_attr, needleWire ? needleWire->attributes : emptyAttr, haystackWire ? haystackWire->attributes : emptyAttr)) @@ -737,7 +737,7 @@ struct ExtractPass : public Pass { RTLIL::Cell *newCell = newMod->addCell(cell->name, cell->type); newCell->parameters = cell->parameters; for (auto &conn : cell->connections()) { - std::vector<RTLIL::SigChunk> chunks = sigmap(conn.second); + std::vector<SigChunk> chunks = sigmap(conn.second); for (auto &chunk : chunks) if (chunk.wire != NULL) chunk.wire = newMod->wires_.at(chunk.wire->name); diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc index dad1c06a..d5b8fe80 100644 --- a/passes/techmap/maccmap.cc +++ b/passes/techmap/maccmap.cc @@ -134,7 +134,7 @@ struct MaccmapWorker } return retval; #else - return std::max(n - 1, 0); + return max(n - 1, 0); #endif } diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc index b250c568..514c3365 100644 --- a/passes/techmap/muxcover.cc +++ b/passes/techmap/muxcover.cc @@ -49,8 +49,8 @@ struct MuxcoverWorker vector<tree_t> tree_list; - dict<std::tuple<SigBit, SigBit, SigBit>, std::tuple<SigBit, pool<SigBit>, bool>> decode_mux_cache; - dict<SigBit, std::tuple<SigBit, SigBit, SigBit>> decode_mux_reverse_cache; + dict<tuple<SigBit, SigBit, SigBit>, tuple<SigBit, pool<SigBit>, bool>> decode_mux_cache; + dict<SigBit, tuple<SigBit, SigBit, SigBit>> decode_mux_reverse_cache; int decode_mux_counter; bool use_mux4; @@ -142,7 +142,7 @@ struct MuxcoverWorker if (A == B) return 0; - std::tuple<SigBit, SigBit, SigBit> key(A, B, sel); + tuple<SigBit, SigBit, SigBit> key(A, B, sel); if (decode_mux_cache.count(key) == 0) { auto &entry = decode_mux_cache[key]; std::get<0>(entry) = module->addWire(NEW_ID); diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc index 0fb5b374..f6ac3964 100644 --- a/passes/techmap/simplemap.cc +++ b/passes/techmap/simplemap.cc @@ -247,7 +247,7 @@ void simplemap_eqne(RTLIL::Module *module, RTLIL::Cell *cell) bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool(); bool is_ne = cell->type == "$ne" || cell->type == "$nex"; - RTLIL::SigSpec xor_out = module->addWire(NEW_ID, std::max(GetSize(sig_a), GetSize(sig_b))); + RTLIL::SigSpec xor_out = module->addWire(NEW_ID, max(GetSize(sig_a), GetSize(sig_b))); RTLIL::Cell *xor_cell = module->addXor(NEW_ID, sig_a, sig_b, xor_out, is_signed); xor_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); simplemap_bitop(module, xor_cell); @@ -293,7 +293,7 @@ void simplemap_tribuf(RTLIL::Module *module, RTLIL::Cell *cell) RTLIL::Cell *gate = module->addCell(NEW_ID, "$_TBUF_"); gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a[i]); - gate->setPort("\\E", sig_e[i]); + gate->setPort("\\E", sig_e); gate->setPort("\\Y", sig_y[i]); } } diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index 592710ed..5334ebfa 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -49,7 +49,7 @@ void apply_prefix(std::string prefix, std::string &id) void apply_prefix(std::string prefix, RTLIL::SigSpec &sig, RTLIL::Module *module) { - std::vector<RTLIL::SigChunk> chunks = sig; + vector<SigChunk> chunks = sig; for (auto &chunk : chunks) if (chunk.wire != NULL) { std::string wire_name = chunk.wire->name.str(); @@ -313,7 +313,7 @@ struct TechmapWorker if (c->type == "$memrd" || c->type == "$memwr" || c->type == "$meminit") { IdString memid = c->getParam("\\MEMID").decode_string(); - log_assert(memory_renames.count(memid)); + log_assert(memory_renames.count(memid) != 0); c->setParam("\\MEMID", Const(memory_renames[memid].str())); } diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index fd5a32e4..a8fcac9b 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -256,7 +256,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, case 2: n = xorshift32(GetSize(sig)); m = xorshift32(GetSize(sig)); - for (int i = std::min(n, m); i < std::max(n, m); i++) + for (int i = min(n, m); i < max(n, m); i++) sig[i] = xorshift32(2) == 1 ? RTLIL::S1 : RTLIL::S0; break; } diff --git a/techlibs/common/.gitignore b/techlibs/common/.gitignore new file mode 100644 index 00000000..0a1e7b68 --- /dev/null +++ b/techlibs/common/.gitignore @@ -0,0 +1,2 @@ +simlib_help.inc +simcells_help.inc diff --git a/techlibs/common/Makefile.inc b/techlibs/common/Makefile.inc index f222a028..236d6c55 100644 --- a/techlibs/common/Makefile.inc +++ b/techlibs/common/Makefile.inc @@ -1,8 +1,24 @@ ifneq ($(SMALL),1) OBJS += techlibs/common/synth.o +OBJS += techlibs/common/prep.o endif +GENFILES += techlibs/common/simlib_help.inc +GENFILES += techlibs/common/simcells_help.inc + +techlibs/common/simlib_help.inc: techlibs/common/cellhelp.py techlibs/common/simlib.v + $(Q) mkdir -p techlibs/common + $(P) python3 $^ > $@.new + $(Q) mv $@.new $@ + +techlibs/common/simcells_help.inc: techlibs/common/cellhelp.py techlibs/common/simcells.v + $(Q) mkdir -p techlibs/common + $(P) python3 $^ > $@.new + $(Q) mv $@.new $@ + +kernel/register.o: techlibs/common/simlib_help.inc techlibs/common/simcells_help.inc + $(eval $(call add_share_file,share,techlibs/common/simlib.v)) $(eval $(call add_share_file,share,techlibs/common/simcells.v)) $(eval $(call add_share_file,share,techlibs/common/techmap.v)) diff --git a/techlibs/common/cellhelp.py b/techlibs/common/cellhelp.py new file mode 100644 index 00000000..5c44cb80 --- /dev/null +++ b/techlibs/common/cellhelp.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +import fileinput +import json + +current_help_msg = [] +current_module_code = [] +current_module_name = None +current_module_signature = None + +def print_current_cell(): + print("cell_help[\"%s\"] = %s;" % (current_module_name, "\n".join([json.dumps(line) for line in current_help_msg]))) + print("cell_code[\"%s+\"] = %s;" % (current_module_name, "\n".join([json.dumps(line) for line in current_module_code]))) + +for line in fileinput.input(): + if line.startswith("//-"): + current_help_msg.append(line[4:] if len(line) > 4 else "\n") + if line.startswith("module "): + current_module_name = line.split()[1].strip("\\") + current_module_signature = " ".join(line.replace("\\", "").replace(";", "").split()[1:]) + current_module_code = [] + elif not line.startswith("endmodule"): + line = " " + line + current_module_code.append(line.replace("\t", " ")) + if line.startswith("endmodule"): + if len(current_help_msg) == 0: + current_help_msg.append("\n") + current_help_msg.append(" %s\n" % current_module_signature) + current_help_msg.append("\n") + current_help_msg.append("No help message for this cell type found.\n") + current_help_msg.append("\n") + print_current_cell() + current_help_msg = [] + diff --git a/techlibs/common/prep.cc b/techlibs/common/prep.cc new file mode 100644 index 00000000..8bae920d --- /dev/null +++ b/techlibs/common/prep.cc @@ -0,0 +1,158 @@ +/* + * 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/register.h" +#include "kernel/celltypes.h" +#include "kernel/rtlil.h" +#include "kernel/log.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +bool check_label(bool &active, std::string run_from, std::string run_to, std::string label) +{ + if (!run_from.empty() && run_from == run_to) { + active = (label == run_from); + } else { + if (label == run_from) + active = true; + if (label == run_to) + active = false; + } + return active; +} + +struct PrepPass : public Pass { + PrepPass() : Pass("prep", "generic synthesis script") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" prep [options]\n"); + log("\n"); + log("This command runs a conservative RTL synthesis. A typical application for this\n"); + log("is the preparation stage of a verification flow. This command does not operate\n"); + log("on partly selected designs.\n"); + log("\n"); + log(" -top <module>\n"); + log(" use the specified module as top module (default='top')\n"); + log("\n"); + log(" -nordff\n"); + log(" passed to 'memory_dff'. prohibits merging of FFs into memory read ports\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("\n"); + log("The following commands are executed by this synthesis command:\n"); + log("\n"); + log(" begin:\n"); + log(" hierarchy -check [-top <top>]\n"); + log("\n"); + log(" prep:\n"); + log(" proc\n"); + log(" opt_const\n"); + log(" opt_clean\n"); + log(" check\n"); + log(" opt -keepdc\n"); + log(" wreduce\n"); + log(" memory_dff [-nordff]\n"); + log(" opt_clean\n"); + log(" memory_collect\n"); + log(" opt -keepdc -fast\n"); + log("\n"); + log(" check:\n"); + log(" stat\n"); + log(" check\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + std::string top_module, memory_opts; + std::string run_from, run_to; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-top" && argidx+1 < args.size()) { + top_module = args[++argidx]; + continue; + } + if (args[argidx] == "-run" && argidx+1 < args.size()) { + size_t pos = args[argidx+1].find(':'); + if (pos == std::string::npos) { + run_from = args[++argidx]; + run_to = args[argidx]; + } else { + run_from = args[++argidx].substr(0, pos); + run_to = args[argidx].substr(pos+1); + } + continue; + } + if (args[argidx] == "-nordff") { + memory_opts += " -nordff"; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!design->full_selection()) + log_cmd_error("This comannd only operates on fully selected designs!\n"); + + bool active = run_from.empty(); + + log_header("Executing PREP pass.\n"); + log_push(); + + if (check_label(active, run_from, run_to, "begin")) + { + if (top_module.empty()) + Pass::call(design, stringf("hierarchy -check")); + else + Pass::call(design, stringf("hierarchy -check -top %s", top_module.c_str())); + } + + if (check_label(active, run_from, run_to, "coarse")) + { + Pass::call(design, "proc"); + Pass::call(design, "opt_const"); + Pass::call(design, "opt_clean"); + Pass::call(design, "check"); + Pass::call(design, "opt -keepdc"); + Pass::call(design, "wreduce"); + Pass::call(design, "memory_dff" + memory_opts); + Pass::call(design, "opt_clean"); + Pass::call(design, "memory_collect"); + Pass::call(design, "opt -keepdc -fast"); + } + + if (check_label(active, run_from, run_to, "check")) + { + Pass::call(design, "stat"); + Pass::call(design, "check"); + } + + log_pop(); + } +} PrepPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/common/simcells.v b/techlibs/common/simcells.v index 3b7d55c6..c4f170a3 100644 --- a/techlibs/common/simcells.v +++ b/techlibs/common/simcells.v @@ -25,60 +25,184 @@ * */ -module \$_BUF_ (A, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_BUF_ (A, Y) +//- +//- A buffer. This cell type is always optimized away by the opt_clean pass. +//- +//- Truth table: A | Y +//- ---+--- +//- 0 | 0 +//- 1 | 1 +//- +module \$_BUF_ (A, Y); input A; output Y; assign Y = A; endmodule -module \$_NOT_ (A, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_NOT_ (A, Y) +//- +//- An inverter gate. +//- +//- Truth table: A | Y +//- ---+--- +//- 0 | 1 +//- 1 | 0 +//- +module \$_NOT_ (A, Y); input A; output Y; assign Y = ~A; endmodule -module \$_AND_ (A, B, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_AND_ (A, B, Y) +//- +//- A 2-input AND gate. +//- +//- Truth table: A B | Y +//- -----+--- +//- 0 0 | 0 +//- 0 1 | 0 +//- 1 0 | 0 +//- 1 1 | 1 +//- +module \$_AND_ (A, B, Y); input A, B; output Y; assign Y = A & B; endmodule -module \$_NAND_ (A, B, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_NAND_ (A, B, Y) +//- +//- A 2-input NAND gate. +//- +//- Truth table: A B | Y +//- -----+--- +//- 0 0 | 1 +//- 0 1 | 1 +//- 1 0 | 1 +//- 1 1 | 0 +//- +module \$_NAND_ (A, B, Y); input A, B; output Y; assign Y = ~(A & B); endmodule -module \$_OR_ (A, B, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_OR_ (A, B, Y) +//- +//- A 2-input OR gate. +//- +//- Truth table: A B | Y +//- -----+--- +//- 0 0 | 0 +//- 0 1 | 1 +//- 1 0 | 1 +//- 1 1 | 1 +//- +module \$_OR_ (A, B, Y); input A, B; output Y; assign Y = A | B; endmodule -module \$_NOR_ (A, B, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_NOR_ (A, B, Y) +//- +//- A 2-input NOR gate. +//- +//- Truth table: A B | Y +//- -----+--- +//- 0 0 | 1 +//- 0 1 | 0 +//- 1 0 | 0 +//- 1 1 | 0 +//- +module \$_NOR_ (A, B, Y); input A, B; output Y; assign Y = ~(A | B); endmodule -module \$_XOR_ (A, B, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_XOR_ (A, B, Y) +//- +//- A 2-input XOR gate. +//- +//- Truth table: A B | Y +//- -----+--- +//- 0 0 | 0 +//- 0 1 | 1 +//- 1 0 | 1 +//- 1 1 | 0 +//- +module \$_XOR_ (A, B, Y); input A, B; output Y; assign Y = A ^ B; endmodule -module \$_XNOR_ (A, B, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_XNOR_ (A, B, Y) +//- +//- A 2-input XNOR gate. +//- +//- Truth table: A B | Y +//- -----+--- +//- 0 0 | 1 +//- 0 1 | 0 +//- 1 0 | 0 +//- 1 1 | 1 +//- +module \$_XNOR_ (A, B, Y); input A, B; output Y; assign Y = ~(A ^ B); endmodule +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_MUX_ (A, B, S, Y) +//- +//- A 2-input MUX gate. +//- +//- Truth table: A B S | Y +//- -------+--- +//- a - 0 | a +//- - b 1 | b +//- module \$_MUX_ (A, B, S, Y); input A, B, S; output Y; assign Y = S ? B : A; endmodule +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_MUX4_ (A, B, C, D, S, T, Y) +//- +//- A 4-input MUX gate. +//- +//- Truth table: A B C D S T | Y +//- -------------+--- +//- a - - - 0 0 | a +//- - b - - 1 0 | b +//- - - c - 0 1 | c +//- - - - d 1 1 | d +//- module \$_MUX4_ (A, B, C, D, S, T, Y); input A, B, C, D, S, T; output Y; @@ -86,6 +210,23 @@ assign Y = T ? (S ? D : C) : (S ? B : A); endmodule +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_MUX8_ (A, B, C, D, E, F, G, H, S, T, U, Y) +//- +//- An 8-input MUX gate. +//- +//- Truth table: A B C D E F G H S T U | Y +//- -----------------------+--- +//- a - - - - - - - 0 0 0 | a +//- - b - - - - - - 1 0 0 | b +//- - - c - - - - - 0 1 0 | c +//- - - - d - - - - 1 1 0 | d +//- - - - - e - - - 0 0 1 | e +//- - - - - - f - - 1 0 1 | f +//- - - - - - - g - 0 1 1 | g +//- - - - - - - - h 1 1 1 | h +//- module \$_MUX8_ (A, B, C, D, E, F, G, H, S, T, U, Y); input A, B, C, D, E, F, G, H, S, T, U; output Y; @@ -95,6 +236,31 @@ assign Y = U ? T ? (S ? H : G) : (S ? B : A); endmodule +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_MUX16_ (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V, Y) +//- +//- A 16-input MUX gate. +//- +//- Truth table: A B C D E F G H I J K L M N O P S T U V | Y +//- -----------------------------------------+--- +//- a - - - - - - - - - - - - - - - 0 0 0 0 | a +//- - b - - - - - - - - - - - - - - 1 0 0 0 | b +//- - - c - - - - - - - - - - - - - 0 1 0 0 | c +//- - - - d - - - - - - - - - - - - 1 1 0 0 | d +//- - - - - e - - - - - - - - - - - 0 0 1 0 | e +//- - - - - - f - - - - - - - - - - 1 0 1 0 | f +//- - - - - - - g - - - - - - - - - 0 1 1 0 | g +//- - - - - - - - h - - - - - - - - 1 1 1 0 | h +//- - - - - - - - - i - - - - - - - 0 0 0 1 | i +//- - - - - - - - - - j - - - - - - 1 0 0 1 | j +//- - - - - - - - - - - k - - - - - 0 1 0 1 | k +//- - - - - - - - - - - - l - - - - 1 1 0 1 | l +//- - - - - - - - - - - - - m - - - 0 0 1 1 | m +//- - - - - - - - - - - - - - n - - 1 0 1 1 | n +//- - - - - - - - - - - - - - - o - 0 1 1 1 | o +//- - - - - - - - - - - - - - - - p 1 1 1 1 | p +//- module \$_MUX16_ (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V, Y); input A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V; output Y; @@ -108,37 +274,145 @@ assign Y = V ? U ? T ? (S ? P : O) : (S ? B : A); endmodule -module \$_AOI3_ (A, B, C, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_AOI3_ (A, B, C, Y) +//- +//- A 3-input And-Or-Invert gate. +//- +//- Truth table: A B C | Y +//- -------+--- +//- 0 0 0 | 1 +//- 0 0 1 | 0 +//- 0 1 0 | 1 +//- 0 1 1 | 0 +//- 1 0 0 | 1 +//- 1 0 1 | 0 +//- 1 1 0 | 0 +//- 1 1 1 | 0 +//- +module \$_AOI3_ (A, B, C, Y); input A, B, C; output Y; assign Y = ~((A & B) | C); endmodule -module \$_OAI3_ (A, B, C, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_OAI3_ (A, B, C, Y) +//- +//- A 3-input Or-And-Invert gate. +//- +//- Truth table: A B C | Y +//- -------+--- +//- 0 0 0 | 1 +//- 0 0 1 | 1 +//- 0 1 0 | 1 +//- 0 1 1 | 0 +//- 1 0 0 | 1 +//- 1 0 1 | 0 +//- 1 1 0 | 1 +//- 1 1 1 | 0 +//- +module \$_OAI3_ (A, B, C, Y); input A, B, C; output Y; assign Y = ~((A | B) & C); endmodule -module \$_AOI4_ (A, B, C, D, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_AOI4_ (A, B, C, Y) +//- +//- A 4-input And-Or-Invert gate. +//- +//- Truth table: A B C D | Y +//- ---------+--- +//- 0 0 0 0 | 1 +//- 0 0 0 1 | 1 +//- 0 0 1 0 | 1 +//- 0 0 1 1 | 0 +//- 0 1 0 0 | 1 +//- 0 1 0 1 | 1 +//- 0 1 1 0 | 1 +//- 0 1 1 1 | 0 +//- 1 0 0 0 | 1 +//- 1 0 0 1 | 1 +//- 1 0 1 0 | 1 +//- 1 0 1 1 | 0 +//- 1 1 0 0 | 0 +//- 1 1 0 1 | 0 +//- 1 1 1 0 | 0 +//- 1 1 1 1 | 0 +//- +module \$_AOI4_ (A, B, C, D, Y); input A, B, C, D; output Y; assign Y = ~((A & B) | (C & D)); endmodule -module \$_OAI4_ (A, B, C, D, Y); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_OAI4_ (A, B, C, Y) +//- +//- A 4-input Or-And-Invert gate. +//- +//- Truth table: A B C D | Y +//- ---------+--- +//- 0 0 0 0 | 1 +//- 0 0 0 1 | 1 +//- 0 0 1 0 | 1 +//- 0 0 1 1 | 1 +//- 0 1 0 0 | 1 +//- 0 1 0 1 | 0 +//- 0 1 1 0 | 0 +//- 0 1 1 1 | 0 +//- 1 0 0 0 | 1 +//- 1 0 0 1 | 0 +//- 1 0 1 0 | 0 +//- 1 0 1 1 | 0 +//- 1 1 0 0 | 1 +//- 1 1 0 1 | 0 +//- 1 1 1 0 | 0 +//- 1 1 1 1 | 0 +//- +module \$_OAI4_ (A, B, C, D, Y); input A, B, C, D; output Y; assign Y = ~((A | B) & (C | D)); endmodule +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_TBUF_ (A, E, Y) +//- +//- A tri-state buffer. +//- +//- Truth table: A E | Y +//- -----+--- +//- a 1 | a +//- - 0 | z +//- module \$_TBUF_ (A, E, Y); input A, E; output Y; assign Y = E ? A : 1'bz; endmodule -module \$_SR_NN_ (S, R, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_SR_NN_ (S, R, Q) +//- +//- A set-reset latch with negative polarity SET and RESET. +//- +//- Truth table: S R | Q +//- -----+--- +//- 0 0 | x +//- 0 1 | 1 +//- 1 0 | 0 +//- 1 1 | y +//- +module \$_SR_NN_ (S, R, Q); input S, R; output reg Q; always @(negedge S, negedge R) begin @@ -149,7 +423,20 @@ always @(negedge S, negedge R) begin end endmodule -module \$_SR_NP_ (S, R, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_SR_NP_ (S, R, Q) +//- +//- A set-reset latch with negative polarity SET and positive polarioty RESET. +//- +//- Truth table: S R | Q +//- -----+--- +//- 0 1 | x +//- 0 0 | 1 +//- 1 1 | 0 +//- 1 0 | y +//- +module \$_SR_NP_ (S, R, Q); input S, R; output reg Q; always @(negedge S, posedge R) begin @@ -160,7 +447,20 @@ always @(negedge S, posedge R) begin end endmodule -module \$_SR_PN_ (S, R, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_SR_PN_ (S, R, Q) +//- +//- A set-reset latch with positive polarity SET and negative polarioty RESET. +//- +//- Truth table: S R | Q +//- -----+--- +//- 1 0 | x +//- 1 1 | 1 +//- 0 0 | 0 +//- 0 1 | y +//- +module \$_SR_PN_ (S, R, Q); input S, R; output reg Q; always @(posedge S, negedge R) begin @@ -171,7 +471,20 @@ always @(posedge S, negedge R) begin end endmodule -module \$_SR_PP_ (S, R, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_SR_PP_ (S, R, Q) +//- +//- A set-reset latch with positive polarity SET and RESET. +//- +//- Truth table: S R | Q +//- -----+--- +//- 1 1 | x +//- 1 0 | 1 +//- 0 1 | 0 +//- 0 0 | y +//- +module \$_SR_PP_ (S, R, Q); input S, R; output reg Q; always @(posedge S, posedge R) begin @@ -182,7 +495,18 @@ always @(posedge S, posedge R) begin end endmodule -module \$_DFF_N_ (D, Q, C); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_N_ (D, C, Q) +//- +//- A negative edge D-type flip-flop. +//- +//- Truth table: D C | Q +//- -----+--- +//- d \ | d +//- - - | q +//- +module \$_DFF_N_ (D, C, Q); input D, C; output reg Q; always @(negedge C) begin @@ -190,7 +514,18 @@ always @(negedge C) begin end endmodule -module \$_DFF_P_ (D, Q, C); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_P_ (D, C, Q) +//- +//- A positive edge D-type flip-flop. +//- +//- Truth table: D C | Q +//- -----+--- +//- d / | d +//- - - | q +//- +module \$_DFF_P_ (D, C, Q); input D, C; output reg Q; always @(posedge C) begin @@ -198,7 +533,18 @@ always @(posedge C) begin end endmodule -module \$_DFFE_NN_ (D, Q, C, E); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFE_NN_ (D, C, E, Q) +//- +//- A negative edge D-type flip-flop with negative polarity enable. +//- +//- Truth table: D C E | Q +//- -------+--- +//- d \ 0 | d +//- - - - | q +//- +module \$_DFFE_NN_ (D, C, E, Q); input D, C, E; output reg Q; always @(negedge C) begin @@ -206,7 +552,18 @@ always @(negedge C) begin end endmodule -module \$_DFFE_NP_ (D, Q, C, E); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFE_NP_ (D, C, E, Q) +//- +//- A negative edge D-type flip-flop with positive polarity enable. +//- +//- Truth table: D C E | Q +//- -------+--- +//- d \ 1 | d +//- - - - | q +//- +module \$_DFFE_NP_ (D, C, E, Q); input D, C, E; output reg Q; always @(negedge C) begin @@ -214,7 +571,18 @@ always @(negedge C) begin end endmodule -module \$_DFFE_PN_ (D, Q, C, E); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFE_PN_ (D, C, E, Q) +//- +//- A positive edge D-type flip-flop with negative polarity enable. +//- +//- Truth table: D C E | Q +//- -------+--- +//- d / 0 | d +//- - - - | q +//- +module \$_DFFE_PN_ (D, C, E, Q); input D, C, E; output reg Q; always @(posedge C) begin @@ -222,7 +590,18 @@ always @(posedge C) begin end endmodule -module \$_DFFE_PP_ (D, Q, C, E); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFE_PP_ (D, C, E, Q) +//- +//- A positive edge D-type flip-flop with positive polarity enable. +//- +//- Truth table: D C E | Q +//- -------+--- +//- d / 1 | d +//- - - - | q +//- +module \$_DFFE_PP_ (D, C, E, Q); input D, C, E; output reg Q; always @(posedge C) begin @@ -230,7 +609,19 @@ always @(posedge C) begin end endmodule -module \$_DFF_NN0_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_NN0_ (D, C, R, Q) +//- +//- A negative edge D-type flip-flop with negative polarity reset. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 0 | 0 +//- d \ - | d +//- - - - | q +//- +module \$_DFF_NN0_ (D, C, R, Q); input D, C, R; output reg Q; always @(negedge C or negedge R) begin @@ -241,7 +632,19 @@ always @(negedge C or negedge R) begin end endmodule -module \$_DFF_NN1_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_NN1_ (D, C, R, Q) +//- +//- A negative edge D-type flip-flop with negative polarity set. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 0 | 1 +//- d \ - | d +//- - - - | q +//- +module \$_DFF_NN1_ (D, C, R, Q); input D, C, R; output reg Q; always @(negedge C or negedge R) begin @@ -252,7 +655,19 @@ always @(negedge C or negedge R) begin end endmodule -module \$_DFF_NP0_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_NP0_ (D, C, R, Q) +//- +//- A negative edge D-type flip-flop with positive polarity reset. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 1 | 0 +//- d \ - | d +//- - - - | q +//- +module \$_DFF_NP0_ (D, C, R, Q); input D, C, R; output reg Q; always @(negedge C or posedge R) begin @@ -263,7 +678,19 @@ always @(negedge C or posedge R) begin end endmodule -module \$_DFF_NP1_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_NP1_ (D, C, R, Q) +//- +//- A negative edge D-type flip-flop with positive polarity set. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 1 | 1 +//- d \ - | d +//- - - - | q +//- +module \$_DFF_NP1_ (D, C, R, Q); input D, C, R; output reg Q; always @(negedge C or posedge R) begin @@ -274,7 +701,19 @@ always @(negedge C or posedge R) begin end endmodule -module \$_DFF_PN0_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_PN0_ (D, C, R, Q) +//- +//- A positive edge D-type flip-flop with negative polarity reset. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 0 | 0 +//- d / - | d +//- - - - | q +//- +module \$_DFF_PN0_ (D, C, R, Q); input D, C, R; output reg Q; always @(posedge C or negedge R) begin @@ -285,7 +724,19 @@ always @(posedge C or negedge R) begin end endmodule -module \$_DFF_PN1_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_PN1_ (D, C, R, Q) +//- +//- A positive edge D-type flip-flop with negative polarity set. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 0 | 1 +//- d / - | d +//- - - - | q +//- +module \$_DFF_PN1_ (D, C, R, Q); input D, C, R; output reg Q; always @(posedge C or negedge R) begin @@ -296,7 +747,19 @@ always @(posedge C or negedge R) begin end endmodule -module \$_DFF_PP0_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_PP0_ (D, C, R, Q) +//- +//- A positive edge D-type flip-flop with positive polarity reset. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 1 | 0 +//- d / - | d +//- - - - | q +//- +module \$_DFF_PP0_ (D, C, R, Q); input D, C, R; output reg Q; always @(posedge C or posedge R) begin @@ -307,7 +770,19 @@ always @(posedge C or posedge R) begin end endmodule -module \$_DFF_PP1_ (D, Q, C, R); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFF_PP1_ (D, C, R, Q) +//- +//- A positive edge D-type flip-flop with positive polarity set. +//- +//- Truth table: D C R | Q +//- -------+--- +//- - - 1 | 1 +//- d / - | d +//- - - - | q +//- +module \$_DFF_PP1_ (D, C, R, Q); input D, C, R; output reg Q; always @(posedge C or posedge R) begin @@ -318,7 +793,20 @@ always @(posedge C or posedge R) begin end endmodule -module \$_DFFSR_NNN_ (C, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFSR_NNN_ (C, S, R, D, Q) +//- +//- A negative edge D-type flip-flop with negative polarity set and reset. +//- +//- Truth table: C S R D | Q +//- ---------+--- +//- - - 0 - | 0 +//- - 0 - - | 1 +//- \ - - d | d +//- - - - - | q +//- +module \$_DFFSR_NNN_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(negedge C, negedge S, negedge R) begin @@ -331,7 +819,21 @@ always @(negedge C, negedge S, negedge R) begin end endmodule -module \$_DFFSR_NNP_ (C, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFSR_NNP_ (C, S, R, D, Q) +//- +//- A negative edge D-type flip-flop with negative polarity set and positive +//- polarity reset. +//- +//- Truth table: C S R D | Q +//- ---------+--- +//- - - 1 - | 0 +//- - 0 - - | 1 +//- \ - - d | d +//- - - - - | q +//- +module \$_DFFSR_NNP_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(negedge C, negedge S, posedge R) begin @@ -344,7 +846,21 @@ always @(negedge C, negedge S, posedge R) begin end endmodule -module \$_DFFSR_NPN_ (C, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFSR_NPN_ (C, S, R, D, Q) +//- +//- A negative edge D-type flip-flop with positive polarity set and negative +//- polarity reset. +//- +//- Truth table: C S R D | Q +//- ---------+--- +//- - - 0 - | 0 +//- - 1 - - | 1 +//- \ - - d | d +//- - - - - | q +//- +module \$_DFFSR_NPN_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(negedge C, posedge S, negedge R) begin @@ -357,7 +873,20 @@ always @(negedge C, posedge S, negedge R) begin end endmodule -module \$_DFFSR_NPP_ (C, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFSR_NPP_ (C, S, R, D, Q) +//- +//- A negative edge D-type flip-flop with positive polarity set and reset. +//- +//- Truth table: C S R D | Q +//- ---------+--- +//- - - 1 - | 0 +//- - 1 - - | 1 +//- \ - - d | d +//- - - - - | q +//- +module \$_DFFSR_NPP_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(negedge C, posedge S, posedge R) begin @@ -370,7 +899,20 @@ always @(negedge C, posedge S, posedge R) begin end endmodule -module \$_DFFSR_PNN_ (C, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFSR_PNN_ (C, S, R, D, Q) +//- +//- A positive edge D-type flip-flop with negative polarity set and reset. +//- +//- Truth table: C S R D | Q +//- ---------+--- +//- - - 0 - | 0 +//- - 0 - - | 1 +//- / - - d | d +//- - - - - | q +//- +module \$_DFFSR_PNN_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(posedge C, negedge S, negedge R) begin @@ -383,7 +925,21 @@ always @(posedge C, negedge S, negedge R) begin end endmodule -module \$_DFFSR_PNP_ (C, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFSR_PNP_ (C, S, R, D, Q) +//- +//- A positive edge D-type flip-flop with negative polarity set and positive +//- polarity reset. +//- +//- Truth table: C S R D | Q +//- ---------+--- +//- - - 1 - | 0 +//- - 0 - - | 1 +//- / - - d | d +//- - - - - | q +//- +module \$_DFFSR_PNP_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(posedge C, negedge S, posedge R) begin @@ -396,7 +952,21 @@ always @(posedge C, negedge S, posedge R) begin end endmodule -module \$_DFFSR_PPN_ (C, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFSR_PPN_ (C, S, R, D, Q) +//- +//- A positive edge D-type flip-flop with positive polarity set and negative +//- polarity reset. +//- +//- Truth table: C S R D | Q +//- ---------+--- +//- - - 0 - | 0 +//- - 1 - - | 1 +//- / - - d | d +//- - - - - | q +//- +module \$_DFFSR_PPN_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(posedge C, posedge S, negedge R) begin @@ -409,7 +979,20 @@ always @(posedge C, posedge S, negedge R) begin end endmodule -module \$_DFFSR_PPP_ (C, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DFFSR_PPP_ (C, S, R, D, Q) +//- +//- A positive edge D-type flip-flop with positive polarity set and reset. +//- +//- Truth table: C S R D | Q +//- ---------+--- +//- - - 1 - | 0 +//- - 1 - - | 1 +//- / - - d | d +//- - - - - | q +//- +module \$_DFFSR_PPP_ (C, S, R, D, Q); input C, S, R, D; output reg Q; always @(posedge C, posedge S, posedge R) begin @@ -422,7 +1005,18 @@ always @(posedge C, posedge S, posedge R) begin end endmodule -module \$_DLATCH_N_ (E, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DLATCH_N_ (E, D, Q) +//- +//- A negative enable D-type latch. +//- +//- Truth table: E D | Q +//- -----+--- +//- 0 d | d +//- - - | q +//- +module \$_DLATCH_N_ (E, D, Q); input E, D; output reg Q; always @* begin @@ -431,7 +1025,18 @@ always @* begin end endmodule -module \$_DLATCH_P_ (E, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DLATCH_P_ (E, D, Q) +//- +//- A positive enable D-type latch. +//- +//- Truth table: E D | Q +//- -----+--- +//- 1 d | d +//- - - | q +//- +module \$_DLATCH_P_ (E, D, Q); input E, D; output reg Q; always @* begin @@ -440,7 +1045,20 @@ always @* begin end endmodule -module \$_DLATCHSR_NNN_ (E, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DLATCHSR_NNN_ (E, S, R, D, Q) +//- +//- A negative enable D-type latch with negative polarity set and reset. +//- +//- Truth table: E S R D | Q +//- ---------+--- +//- - - 0 - | 0 +//- - 0 - - | 1 +//- 0 - - d | d +//- - - - - | q +//- +module \$_DLATCHSR_NNN_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -453,7 +1071,21 @@ always @* begin end endmodule -module \$_DLATCHSR_NNP_ (E, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DLATCHSR_NNP_ (E, S, R, D, Q) +//- +//- A negative enable D-type latch with negative polarity set and positive polarity +//- reset. +//- +//- Truth table: E S R D | Q +//- ---------+--- +//- - - 1 - | 0 +//- - 0 - - | 1 +//- 0 - - d | d +//- - - - - | q +//- +module \$_DLATCHSR_NNP_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -466,7 +1098,21 @@ always @* begin end endmodule -module \$_DLATCHSR_NPN_ (E, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DLATCHSR_NPN_ (E, S, R, D, Q) +//- +//- A negative enable D-type latch with positive polarity set and negative polarity +//- reset. +//- +//- Truth table: E S R D | Q +//- ---------+--- +//- - - 0 - | 0 +//- - 1 - - | 1 +//- 0 - - d | d +//- - - - - | q +//- +module \$_DLATCHSR_NPN_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -479,7 +1125,20 @@ always @* begin end endmodule -module \$_DLATCHSR_NPP_ (E, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DLATCHSR_NPP_ (E, S, R, D, Q) +//- +//- A negative enable D-type latch with positive polarity set and reset. +//- +//- Truth table: E S R D | Q +//- ---------+--- +//- - - 1 - | 0 +//- - 1 - - | 1 +//- 0 - - d | d +//- - - - - | q +//- +module \$_DLATCHSR_NPP_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -492,7 +1151,20 @@ always @* begin end endmodule -module \$_DLATCHSR_PNN_ (E, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DLATCHSR_PNN_ (E, S, R, D, Q) +//- +//- A positive enable D-type latch with negative polarity set and reset. +//- +//- Truth table: E S R D | Q +//- ---------+--- +//- - - 0 - | 0 +//- - 0 - - | 1 +//- 1 - - d | d +//- - - - - | q +//- +module \$_DLATCHSR_PNN_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -505,7 +1177,21 @@ always @* begin end endmodule -module \$_DLATCHSR_PNP_ (E, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DLATCHSR_PNP_ (E, S, R, D, Q) +//- +//- A positive enable D-type latch with negative polarity set and positive polarity +//- reset. +//- +//- Truth table: E S R D | Q +//- ---------+--- +//- - - 1 - | 0 +//- - 0 - - | 1 +//- 1 - - d | d +//- - - - - | q +//- +module \$_DLATCHSR_PNP_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -518,7 +1204,21 @@ always @* begin end endmodule -module \$_DLATCHSR_PPN_ (E, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DLATCHSR_PPN_ (E, S, R, D, Q) +//- +//- A positive enable D-type latch with positive polarity set and negative polarity +//- reset. +//- +//- Truth table: E S R D | Q +//- ---------+--- +//- - - 0 - | 0 +//- - 1 - - | 1 +//- 1 - - d | d +//- - - - - | q +//- +module \$_DLATCHSR_PPN_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -531,7 +1231,20 @@ always @* begin end endmodule -module \$_DLATCHSR_PPP_ (E, S, R, D, Q); +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_DLATCHSR_PPP_ (E, S, R, D, Q) +//- +//- A positive enable D-type latch with positive polarity set and reset. +//- +//- Truth table: E S R D | Q +//- ---------+--- +//- - - 1 - | 0 +//- - 1 - - | 1 +//- 1 - - d | d +//- - - - - | q +//- +module \$_DLATCHSR_PPP_ (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index 9d71feef..83d00f32 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -81,6 +81,7 @@ struct SynthPass : public Pass { log("\n"); log(" coarse:\n"); log(" proc\n"); + log(" opt_const\n"); log(" opt_clean\n"); log(" check\n"); log(" opt\n"); @@ -179,6 +180,7 @@ struct SynthPass : public Pass { if (check_label(active, run_from, run_to, "coarse")) { Pass::call(design, "proc"); + Pass::call(design, "opt_const"); Pass::call(design, "opt_clean"); Pass::call(design, "check"); Pass::call(design, "opt"); diff --git a/techlibs/ice40/Makefile.inc b/techlibs/ice40/Makefile.inc index 63d6086a..83009d17 100644 --- a/techlibs/ice40/Makefile.inc +++ b/techlibs/ice40/Makefile.inc @@ -1,6 +1,7 @@ OBJS += techlibs/ice40/synth_ice40.o OBJS += techlibs/ice40/ice40_ffssr.o +OBJS += techlibs/ice40/ice40_ffinit.o OBJS += techlibs/ice40/ice40_opt.o GENFILES += techlibs/ice40/brams_init1.vh diff --git a/techlibs/ice40/brams_map.v b/techlibs/ice40/brams_map.v index a82161c9..19a61d73 100644 --- a/techlibs/ice40/brams_map.v +++ b/techlibs/ice40/brams_map.v @@ -213,14 +213,14 @@ module \$__ICE40_RAM4K_M0 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1E .RDATA(A1DATA), .RADDR(A1ADDR_11), .RCLK(CLK2), - .RCLKE(1'b1), - .RE(A1EN), + .RCLKE(A1EN), + .RE(1'b1), .WDATA(B1DATA), .WADDR(B1ADDR_11), .MASK(~B1EN), .WCLK(CLK3), - .WCLKE(1'b1), - .WE(|B1EN) + .WCLKE(|B1EN), + .WE(1'b1) ); endmodule @@ -299,13 +299,13 @@ module \$__ICE40_RAM4K_M123 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B .RDATA(A1DATA_16), .RADDR(A1ADDR_11), .RCLK(CLK2), - .RCLKE(1'b1), - .RE(A1EN), + .RCLKE(A1EN), + .RE(1'b1), .WDATA(B1DATA_16), .WADDR(B1ADDR_11), .WCLK(CLK3), - .WCLKE(1'b1), - .WE(|B1EN) + .WCLKE(|B1EN), + .WE(1'b1) ); endmodule diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v index 998ad3a1..7778b551 100644 --- a/techlibs/ice40/cells_sim.v +++ b/techlibs/ice40/cells_sim.v @@ -47,19 +47,25 @@ module SB_IO ( din_1 = din_q_1; end + // work around simulation glitches on dout in DDR mode + reg outclk_delayed_1; + reg outclk_delayed_2; + always @* outclk_delayed_1 <= OUTPUT_CLK; + always @* outclk_delayed_2 <= outclk_delayed_1; + always @* begin if (PIN_TYPE[3]) dout = PIN_TYPE[2] ? !dout_q_0 : D_OUT_0; else - dout = (OUTPUT_CLK ^ NEG_TRIGGER) || PIN_TYPE[2] ? dout_q_0 : dout_q_1; + dout = (outclk_delayed_2 ^ NEG_TRIGGER) || PIN_TYPE[2] ? dout_q_0 : dout_q_1; end assign D_IN_0 = din_0, D_IN_1 = din_1; generate if (PIN_TYPE[5:4] == 2'b01) assign PACKAGE_PIN = dout; - if (PIN_TYPE[5:4] == 2'b10) assign PACKAGE_PIN = outena_q ? dout : 1'bz; - if (PIN_TYPE[5:4] == 2'b11) assign PACKAGE_PIN = OUTPUT_ENABLE ? dout : 1'bz; + if (PIN_TYPE[5:4] == 2'b10) assign PACKAGE_PIN = OUTPUT_ENABLE ? dout : 1'bz; + if (PIN_TYPE[5:4] == 2'b11) assign PACKAGE_PIN = outena_q ? dout : 1'bz; endgenerate `endif endmodule @@ -661,7 +667,7 @@ endmodule module ICESTORM_LC ( input I0, I1, I2, I3, CIN, CLK, CEN, SR, - output O, COUT + output LO, O, COUT ); parameter [15:0] LUT_INIT = 0; @@ -678,6 +684,8 @@ module ICESTORM_LC ( wire [1:0] lut_s1 = I1 ? lut_s2[ 3:2] : lut_s2[1:0]; wire lut_o = I0 ? lut_s1[ 1] : lut_s1[ 0]; + assign LO = lut_o; + wire polarized_clk; assign polarized_clk = CLK ^ NEG_CLK; diff --git a/techlibs/ice40/ice40_ffinit.cc b/techlibs/ice40/ice40_ffinit.cc new file mode 100644 index 00000000..8c4b9a37 --- /dev/null +++ b/techlibs/ice40/ice40_ffinit.cc @@ -0,0 +1,163 @@ +/* + * 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 + +struct Ice40FfinitPass : public Pass { + Ice40FfinitPass() : Pass("ice40_ffinit", "iCE40: handle FF init values") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" ice40_ffinit [options] [selection]\n"); + log("\n"); + log("Remove zero init values for FF output signals. Add inverters to implement\n"); + log("nonzero init values.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header("Executing ICE40_FFINIT pass (implement FF init values).\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()) + { + log("Handling FF init values in %s.\n", log_id(module)); + + SigMap sigmap(module); + pool<Wire*> init_wires; + dict<SigBit, State> initbits; + pool<SigBit> handled_initbits; + + for (auto wire : module->selected_wires()) + { + if (wire->attributes.count("\\init") == 0) + continue; + + SigSpec wirebits = sigmap(wire); + Const initval = wire->attributes.at("\\init"); + init_wires.insert(wire); + + for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++) + { + SigBit bit = wirebits[i]; + State val = initval[i]; + + if (val != State::S0 && val != State::S1) + continue; + + if (initbits.count(bit)) { + if (initbits.at(bit) != val) + log_error("Conflicting init values for signal %s.\n", log_signal(bit)); + continue; + } + + initbits[bit] = val; + } + } + + pool<IdString> sb_dff_types = { + "\\SB_DFF", "\\SB_DFFE", "\\SB_DFFSR", "\\SB_DFFR", "\\SB_DFFSS", "\\SB_DFFS", "\\SB_DFFESR", + "\\SB_DFFER", "\\SB_DFFESS", "\\SB_DFFES", "\\SB_DFFN", "\\SB_DFFNE", "\\SB_DFFNSR", "\\SB_DFFNR", + "\\SB_DFFNSS", "\\SB_DFFNS", "\\SB_DFFNESR", "\\SB_DFFNER", "\\SB_DFFNESS", "\\SB_DFFNES" + }; + + for (auto cell : module->selected_cells()) + { + if (!sb_dff_types.count(cell->type)) + continue; + + SigBit sig_d = sigmap(cell->getPort("\\D")); + SigBit sig_q = sigmap(cell->getPort("\\Q")); + + if (!initbits.count(sig_q)) + continue; + + State val = initbits.at(sig_q); + handled_initbits.insert(sig_q); + + log("FF init value for cell %s (%s): %s = %c\n", log_id(cell), log_id(cell->type), + log_signal(sig_q), val != State::S0 ? '1' : '0'); + + if (val == State::S0) + continue; + + string type_str = cell->type.str(); + + if (type_str.back() == 'S') { + type_str.back() = 'R'; + cell->type = type_str; + cell->setPort("\\R", cell->getPort("\\S")); + cell->unsetPort("\\S"); + } else + if (type_str.back() == 'R') { + type_str.back() = 'S'; + cell->type = type_str; + cell->setPort("\\S", cell->getPort("\\R")); + cell->unsetPort("\\R"); + } + + Wire *new_sig_d = module->addWire(NEW_ID); + Wire *new_sig_q = module->addWire(NEW_ID); + + module->addNotGate(NEW_ID, sig_d, new_sig_d); + module->addNotGate(NEW_ID, new_sig_q, sig_q); + + cell->setPort("\\D", new_sig_d); + cell->setPort("\\Q", new_sig_q); + } + + for (auto wire : init_wires) + { + if (wire->attributes.count("\\init") == 0) + continue; + + SigSpec wirebits = sigmap(wire); + Const &initval = wire->attributes.at("\\init"); + bool remove_attribute = true; + + for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++) { + if (handled_initbits.count(wirebits[i])) + initval[i] = State::Sx; + else if (initval[i] != State::Sx) + remove_attribute = false; + } + + if (remove_attribute) + wire->attributes.erase("\\init"); + } + } + } +} Ice40FfinitPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/ice40/ice40_opt.cc b/techlibs/ice40/ice40_opt.cc index 6acefaf4..677ac8d7 100644 --- a/techlibs/ice40/ice40_opt.cc +++ b/techlibs/ice40/ice40_opt.cc @@ -76,13 +76,24 @@ static void run_ice40_opts(Module *module) for (auto cell : sb_lut_cells) { - if (optimized_co.count(sigmap(cell->getPort("\\I0")))) goto remap_lut; - if (optimized_co.count(sigmap(cell->getPort("\\I1")))) goto remap_lut; - if (optimized_co.count(sigmap(cell->getPort("\\I2")))) goto remap_lut; - if (optimized_co.count(sigmap(cell->getPort("\\I3")))) goto remap_lut; - continue; + SigSpec inbits; + + inbits.append(cell->getPort("\\I0")); + inbits.append(cell->getPort("\\I1")); + inbits.append(cell->getPort("\\I2")); + inbits.append(cell->getPort("\\I3")); + sigmap.apply(inbits); + + if (optimized_co.count(inbits[0])) goto remap_lut; + if (optimized_co.count(inbits[1])) goto remap_lut; + if (optimized_co.count(inbits[2])) goto remap_lut; + if (optimized_co.count(inbits[3])) goto remap_lut; + + if (!sigmap(inbits).is_fully_const()) + continue; remap_lut: + module->design->scratchpad_set_bool("opt.did_something", true); log("Mapping SB_LUT4 cell %s.%s back to logic.\n", log_id(module), log_id(cell)); cell->type ="$lut"; diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index 788835f1..92d53f4a 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -72,6 +72,9 @@ struct SynthIce40Pass : public Pass { log(" -nobram\n"); log(" do not use SB_RAM40_4K* cells in output netlist\n"); log("\n"); + log(" -abc2\n"); + log(" run two passes of 'abc' for slightly improved logic density\n"); + log("\n"); log("\n"); log("The following commands are executed by this synthesis command:\n"); log("\n"); @@ -100,14 +103,18 @@ struct SynthIce40Pass : public Pass { log(" ice40_opt\n"); log("\n"); log(" map_ffs:\n"); + log(" dffsr2dff\n"); log(" dff2dffe -direct-match $_DFF_*\n"); log(" techmap -map +/ice40/cells_map.v\n"); log(" opt_const -mux_undef\n"); log(" simplemap\n"); + log(" ice40_ffinit\n"); log(" ice40_ffssr\n"); log(" ice40_opt -full\n"); log("\n"); log(" map_luts:\n"); + log(" abc (only if -abc2)\n"); + log(" ice40_opt (only if -abc2)\n"); log(" abc -lut 4\n"); log(" clean\n"); log("\n"); @@ -136,6 +143,7 @@ struct SynthIce40Pass : public Pass { bool nobram = false; bool flatten = true; bool retime = false; + bool abc2 = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -180,6 +188,10 @@ struct SynthIce40Pass : public Pass { nobram = true; continue; } + if (args[argidx] == "-abc2") { + abc2 = true; + continue; + } break; } extra_args(args, argidx, design); @@ -232,16 +244,22 @@ struct SynthIce40Pass : public Pass { if (check_label(active, run_from, run_to, "map_ffs")) { + Pass::call(design, "dffsr2dff"); Pass::call(design, "dff2dffe -direct-match $_DFF_*"); Pass::call(design, "techmap -map +/ice40/cells_map.v"); Pass::call(design, "opt_const -mux_undef"); Pass::call(design, "simplemap"); + Pass::call(design, "ice40_ffinit"); Pass::call(design, "ice40_ffssr"); Pass::call(design, "ice40_opt -full"); } if (check_label(active, run_from, run_to, "map_luts")) { + if (abc2) { + Pass::call(design, "abc"); + Pass::call(design, "ice40_opt"); + } Pass::call(design, "abc -lut 4"); Pass::call(design, "clean"); } diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index ee67beba..21d1fb1e 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -91,13 +91,14 @@ struct SynthXilinxPass : public Pass { log(" fine:\n"); log(" opt -fast -full\n"); log(" memory_map\n"); + log(" dffsr2dff\n"); log(" dff2dffe\n"); log(" opt -full\n"); log(" techmap -map +/techmap.v -map +/xilinx/arith_map.v\n"); log(" opt -fast\n"); log("\n"); log(" map_luts:\n"); - log(" abc -lut 5:8 [-dff]\n"); + log(" abc -luts 2:2,3,6:5,10,20 [-dff]\n"); log(" clean\n"); log("\n"); log(" map_cells:\n"); @@ -196,6 +197,7 @@ struct SynthXilinxPass : public Pass { { Pass::call(design, "opt -fast -full"); Pass::call(design, "memory_map"); + Pass::call(design, "dffsr2dff"); Pass::call(design, "dff2dffe"); Pass::call(design, "opt -full"); Pass::call(design, "techmap -map +/techmap.v -map +/xilinx/arith_map.v"); @@ -204,7 +206,7 @@ struct SynthXilinxPass : public Pass { if (check_label(active, run_from, run_to, "map_luts")) { - Pass::call(design, "abc -lut 5:8" + string(retime ? " -dff" : "")); + Pass::call(design, "abc -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : "")); Pass::call(design, "clean"); } diff --git a/tests/simple/graphtest.v b/tests/simple/graphtest.v new file mode 100644 index 00000000..74788dbb --- /dev/null +++ b/tests/simple/graphtest.v @@ -0,0 +1,34 @@ +module graphtest (A,B,X,Y,Z); + +input [3:0] A; +input [3:0] B; +output reg [3:0] X; +output [9:0] Y; +output [7:0] Z; + +wire [4:0] t; + +assign t[4] = 1'b0; // Constant connects to wire +assign t[2:0] = A[2:0] & { 2'b10, B[3]}; // Concatenation of intermediate wire +assign t[3] = A[2] ^ B[3]; // Bitwise-XOR + +// assign Y[2:0] = 3'b111; +// assign Y[6:3] = A; +// assign Y[9:7] = t[0:2]; +assign Y = {3'b111, A, t[2:0]}; // Direct assignment of concatenation + +assign Z[0] = 1'b0; // Constant connects to PO +assign Z[1] = t[3]; // Intermediate sig connects to PO +assign Z[3:2] = A[2:1]; // PI connects to PO +assign Z[7:4] = {1'b0, B[2:0]}; // Concat of CV and PI connect to PO + +always @* begin + if (A == 4'b1111) begin // All-Const at port (eq) + X = B; + end + else begin + X = 4'b0000; // All-Const at port (mux) + end +end + +endmodule diff --git a/tests/simple/memory.v b/tests/simple/memory.v index 67f89cd7..d58ed9d1 100644 --- a/tests/simple/memory.v +++ b/tests/simple/memory.v @@ -228,3 +228,18 @@ module memtest09 ( end endmodule +// ---------------------------------------------------------- + +module memtest10(input clk, input [5:0] din, output [5:0] dout); + reg [5:0] queue [0:3]; + integer i; + + always @(posedge clk) begin + queue[0] <= din; + for (i = 1; i < 4; i=i+1) begin + queue[i] <= queue[i-1]; + end + end + + assign dout = queue[3]; +endmodule diff --git a/tests/simple/task_func.v b/tests/simple/task_func.v index 9b8e26e5..fa50c1d5 100644 --- a/tests/simple/task_func.v +++ b/tests/simple/task_func.v @@ -68,7 +68,7 @@ endmodule // ------------------------------------------------------------------- -module task_func_test03( input [7:0] din_a, input [7:0] din_b, output [7:0] dout_a); +module task_func_test03(input [7:0] din_a, input [7:0] din_b, output [7:0] dout_a); assign dout_a = test(din_a,din_b); function [7:0] test; input [7:0] a; @@ -80,3 +80,43 @@ module task_func_test03( input [7:0] din_a, input [7:0] din_b, output [7:0] dout end endfunction endmodule + +// ------------------------------------------------------------------- + +module task_func_test04(input [7:0] in, output [7:0] out1, out2, out3, out4); + parameter p = 23; + parameter px = 42; + function [7:0] test1; + input [7:0] i; + parameter p = 42; + begin + test1 = i + p; + end + endfunction + function [7:0] test2; + input [7:0] i; + parameter p2 = p+42; + begin + test2 = i + p2; + end + endfunction + function [7:0] test3; + input [7:0] i; + begin + test3 = i + p; + end + endfunction + function [7:0] test4; + input [7:0] i; + parameter px = p + 13; + parameter p3 = px - 37; + parameter p4 = p3 ^ px; + begin + test4 = i + p4; + end + endfunction + assign out1 = test1(in); + assign out2 = test2(in); + assign out3 = test3(in); + assign out4 = test4(in); +endmodule diff --git a/tests/simple/wreduce.v b/tests/simple/wreduce.v new file mode 100644 index 00000000..ba548438 --- /dev/null +++ b/tests/simple/wreduce.v @@ -0,0 +1,9 @@ +module wreduce_test0(input [7:0] a, b, output [15:0] x, y, z); + assign x = -$signed({1'b0, a}); + assign y = $signed({1'b0, a}) + $signed({1'b0, b}); + assign z = x ^ y; +endmodule + +module wreduce_test1(input [31:0] a, b, output [7:0] x, y, z, w); + assign x = a - b, y = a * b, z = a >> b, w = a << b; +endmodule |