diff options
364 files changed, 29403 insertions, 4790 deletions
diff --git a/.gitignore b/.gitignore index 841b0ffa2..93e28cd6c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,17 +11,19 @@ /qtcreator.creator.user /Makefile.conf /abc +/viz.js /yosys /yosys.exe -/yosys.html +/yosys.js /yosys-abc /yosys-abc.exe /yosys-config +/yosys-smtbmc /yosys-filterlib /yosys-filterlib.exe -/yosys-filterlib.html /kernel/version_*.cc /share -/libyosys /yosys-win32-mxebin-* /yosys-win32-vcxsrc-* +/yosysjs-* +/libyosys.so diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..9f0cc06e7 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,34 @@ +sudo: false +script: make && make test +language: cpp +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gperf + - build-essential + - clang + - bison + - flex + - libreadline-dev + - gawk + - tcl-dev + - libffi-dev + - git + - mercurial + - graphviz + - xdot + - pkg-config + - python + - g++-4.8 +before_install: + - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi + - git clone git://github.com/steveicarus/iverilog.git + - (cd iverilog && autoconf && ./configure --prefix=$HOME/iverilog && make && make install) + - export PATH=$PATH:$HOME/iverilog/bin +compiler: +# - clang + - gcc +os: + - linux @@ -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 ---------------------- @@ -82,8 +170,8 @@ Yosys 0.3.0 .. Yosys 0.4 * Changes for simple synthesis flows - There is now a "synth" command with a recommended default script - Many improvements in synthesis of arithmetic functions to gates - - Multiplieres and adders with many operands are using carry-save adder trees - - Remaining adders are now implemented using Brent–Kung carry look-ahead adders + - Multipliers and adders with many operands are using carry-save adder trees + - Remaining adders are now implemented using Brent-Kung carry look-ahead adders - Various new high-level optimizations on RTL netlist - Various improvements in FSM optimization - Updated ABC to hg 5b5af75f1dda (from 2014-11-07) @@ -98,7 +186,7 @@ Yosys 0.3.0 .. Yosys 0.4 - Added macros for code coverage counters - Added some Makefile magic for pretty make logs - Added "kernel/yosys.h" with all the core definitions - - Chanded a lot of code from FILE* to c++ streams + - Changed a lot of code from FILE* to c++ streams - Added RTLIL::Monitor API and "trace" command - Added "Yosys" C++ namespace @@ -167,7 +255,7 @@ Yosys 0.2.0 .. Yosys 0.3.0 - Added "sat -dump_cnf" feature - Added "sat -initsteps <N>" feature - Added "freduce -stop <N>" feature - - Added "fredure -dump <prefix>" feature + - Added "freduce -dump <prefix>" feature * Integration with ABC: - Updated ABC rev to 7600ffb9340c @@ -254,13 +342,13 @@ Yosys 0.1.0 .. Yosys 0.2.0 - Added "expose" command - Added support for @<sel_name> to sat and eval signal expressions - * Changes in the 'make test' framework and auxilary test tools: + * Changes in the 'make test' framework and auxiliary test tools: - Added autotest.sh -p and -f options - Replaced autotest.sh ISIM support with XSIM support - Added test cases for SAT framework * Added "abbreviated IDs": - - Now $<something>$foo can be abbriviated as $foo. + - Now $<something>$foo can be abbreviated as $foo. - Usually this last part is a unique id (from RTLIL::autoidx) - This abbreviated IDs are now also used in "show" output diff --git a/CodeOfConduct b/CodeOfConduct new file mode 100644 index 000000000..4f779977b --- /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 46624e9c7..cbe1fb8be 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 @@ -154,6 +157,41 @@ only use one wire from such a group of connected wires. For example: log("%d\n", sigmap(a) == sigmap(b)); // will print 1 +Using the RTLIL Netlist Format +------------------------------ + +In the RTLIL netlist format the cell ports contain SigSpecs that point to the +Wires. There are no references in the other direction. This has two direct +consequences: + +(1) It is very easy to go from cells to wires but hard to go in the other way. + +(2) There is no danger in removing cells from the netlists, but removing wires +can break the netlist format when there are still references to the wire +somewhere in the netlist. + +The solution to (1) is easy: Create custom indexes that allow you to make fast +lookups for the wire-to-cell direction. You can either use existing generic +index structures to do that (such as the ModIndex class) or write your own +index. For many application it is simplest to construct a custom index. For +example: + + SigMap sigmap(module); + dict<SigBit, Cell*> sigbit_to_driver_index; + + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + sigbit_to_driver_index[bit] = cell; + +Regarding (2): There is a general theme in Yosys that you don't remove wires +from the design. You can rename them, unconnect them, but you do not actually remove +the Wire object from the module. Instead you let the "clean" command take care +of the dangling wires. On the other hand it is safe to remove cells (as long as +you make sure this does not invalidate a custom index you are using in your code). + + Example Code ------------ @@ -193,15 +231,15 @@ Formatting of code on its own line for larger blocks, especially blocks that contains blank lines. -- Otherwise stick to the Linux Kernel Coding Stlye: +- Otherwise stick to the Linux Kernel Coding Style: https://www.kernel.org/doc/Documentation/CodingStyle -C++ Langugage +C++ Language ------------- Yosys is written in C++11. At the moment only constructs supported by -gcc 4.6 are allowed in Yosys code. This will change in future releases. +gcc 4.8 are allowed in Yosys code. This will change in future releases. In general Yosys uses "int" instead of "size_t". To avoid compiler warnings for implicit type casts, always use "GetSize(foobar)" instead @@ -227,7 +265,7 @@ Creating the Visual Studio Template Project [ ] Add to source control [X] Console applications - [X] Empty Projcect + [X] Empty Project [ ] SDL checks 2. Open YosysVS Project Properties @@ -268,7 +306,7 @@ Things to do after finalizing the cell interface: - Add support to kernel/satgen.h for the new cell type - Add to manual/CHAPTER_CellLib.tex (or just add a fixme to the bottom) - - Maybe add support to the verilog backend for dumping such cells as expression + - Maybe add support to the Verilog backend for dumping such cells as expression @@ -282,22 +320,18 @@ Update the CHANGELOG file: vi CHANGELOG -Run all tests with "make config-{clang,gcc,gcc-4.6}": +Update and check documentation: cd ~yosys - make clean - make test - make vloghtb - make install - - cd ~yosys-bigsim - make clean - make full + make update-manual + make manual + - sanity check the figures in the appnotes and presentation + - if there are any odd things -> investigate + - make cosmetic changes to the .tex files if necessary - cd ~vloghammer - make purge gen_issues gen_samples - make SYN_LIST="yosys" SIM_LIST="icarus yosim verilator" REPORT_FULL=1 world - chromium-browser report.html + cd ~yosys + vi README CodingReadme + - is the information provided in those file still up to date Then with default config setting: @@ -311,28 +345,20 @@ 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 - make manual - - sanity check the figures in the appnotes and presentation - - if there are any odd things -> investigate - - make cosmetic changes to the .tex files if necessary - - -Also with default config setting: - - cd ~yosys/techlibs/cmos + cd ~yosys/examples/cmos bash testbench.sh - cd ~yosys/techlibs/xilinx/example_basys3 + cd ~yosys/examples/basys3 bash run.sh Test building plugins with various of the standard passes: yosys-config --build test.so equiv_simple.cc + - also check the code examples in CodingReadme -Finally if a current verific library is available: +And if a version of the verific library is currently available: cd ~yosys cat frontends/verific/build_amd64.txt @@ -342,12 +368,22 @@ Finally if a current verific library is available: ../../yosys test_navre.ys -Release candiate: +Finally run all tests with "make config-{clang,gcc,gcc-4.8}": - - create branch yosys-x.y.z-rc and push to github - - contact the usual suspects per mail and ask them to test - - post on the reddit and ask people to test - - commit KISS fixes to the -rc branch if necessary + cd ~yosys + make clean + make test + make vloghtb + make install + + cd ~yosys-bigsim + make clean + make full + + cd ~vloghammer + make purge gen_issues gen_samples + make SYN_LIST="yosys" SIM_LIST="icarus yosim verilator" REPORT_FULL=1 world + chromium-browser report.html Release: @@ -359,7 +395,6 @@ Release: - push tag to github - post changelog on github - post short release note on reddit - - delete -rc branch from github Updating the website: @@ -376,12 +411,3 @@ Updating the website: git commit -am update make push - -In master branch: - - git merge {release-tag} - - set version to x.y.z+ in Makefile - - add section "Yosys x.y.z .. x.y.z+" to CHANGELOG - git commit --amend -am "Yosys x.y.z+" - - @@ -1,7 +1,7 @@ CONFIG := icc # CONFIG := gcc -# CONFIG := gcc-4.6 +# CONFIG := gcc-4.8 # CONFIG := emcc # CONFIG := mxe @@ -12,16 +12,26 @@ ENABLE_PLUGINS := 1 ENABLE_READLINE := 1 ENABLE_VERIFIC := 0 ENABLE_COVER := 1 +ENABLE_LIBYOSYS := 0 # other configuration flags ENABLE_GPROF := 0 ENABLE_NDEBUG := 0 -DESTDIR := /usr/local +# clang sanitizers +SANITIZER = +# SANITIZER = address +# SANITIZER = memory +# SANITIZER = undefined +# SANITIZER = cfi + + +PREFIX ?= /usr/local INSTALL_SUDO := -TARGET_BINDIR := $(DESTDIR)/bin -TARGET_DATDIR := $(DESTDIR)/share/yosys +BINDIR := $(PREFIX)/bin +LIBDIR := $(PREFIX)/lib +DATDIR := $(PREFIX)/share/yosys EXE = OBJS = @@ -35,10 +45,14 @@ SMALL = 0 all: top-all -YOSYS_SRC := $(shell pwd) -CXXFLAGS = -Wall -Wextra -ggdb -I"$(YOSYS_SRC)" -MD -D_YOSYS_ -fPIC -I$(DESTDIR)/include -LDFLAGS = -L$(DESTDIR)/lib +YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST))) +VPATH := $(YOSYS_SRC) + +CXXFLAGS += -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -D_YOSYS_ -fPIC -I$(PREFIX)/include +LDFLAGS += -L$(LIBDIR) LDLIBS = -lstdc++ -lm + +PKG_CONFIG = pkg-config SED = sed BISON = bison @@ -51,14 +65,14 @@ 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 -GIT_REV := $(shell git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN) +YOSYS_VER := 0.6+$(shell test -d .git && { git log --author=clifford@clifford.at --oneline 5869d26da021.. | wc -l; }) +GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN) OBJS = kernel/version_$(GIT_REV).o # set 'ABCREV = default' to use abc/ as it is @@ -67,9 +81,13 @@ 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 = 61ad5f908c03 +ABCREV = b5df6e2b76f0 ABCPULL = 1 -ABCMKARGS = # CC="$(CXX)" CXX="$(CXX)" +ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" + +# set ABCEXTERNAL = <abc-command> to use an external ABC instance +# Note: The in-tree ABC (yosys-abc) will not be installed when ABCEXTERNAL is set. +ABCEXTERNAL = define newline @@ -83,30 +101,75 @@ 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 -fsanitize-memory-track-origins +LDFLAGS += -fPIE -fsanitize-memory-track-origins +endif +ifeq ($(SANITIZER),cfi) +CXXFLAGS += -flto +LDFLAGS += -flto +endif +endif + else ifeq ($(CONFIG),gcc) CXX = gcc -CXXFLAGS += -std=gnu++0x -Os +LD = gcc +CXXFLAGS += -std=c++11 -Os -else ifeq ($(CONFIG),gcc-4.6) -CXX = gcc-4.6 -CXXFLAGS += -std=gnu++0x -Os +else ifeq ($(CONFIG),gcc-4.8) +CXX = gcc-4.8 +LD = gcc-4.8 +CXXFLAGS += -std=c++11 -Os else ifeq ($(CONFIG),emcc) CXX = emcc -CXXFLAGS += -std=c++11 -Os -Wno-warn-absolute-paths -CXXFLAGS := $(filter-out -ggdb,$(CXXFLAGS)) -EXE = .html +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 +EMCCFLAGS += -s EXPORTED_FUNCTIONS="['_main','_run','_prompt','_errmsg']" +EMCCFLAGS += -s TOTAL_MEMORY=128*1024*1024 +# https://github.com/kripken/emscripten/blob/master/src/settings.js +CXXFLAGS += $(EMCCFLAGS) +LDFLAGS += $(EMCCFLAGS) +LDLIBS = +EXE = .js + +TARGETS := $(filter-out yosys-config,$(TARGETS)) +EXTRA_TARGETS += yosysjs-$(YOSYS_VER).zip + +viz.js: + wget -O viz.js.part https://github.com/mdaines/viz.js/releases/download/0.0.3/viz.js + mv viz.js.part viz.js + +yosysjs-$(YOSYS_VER).zip: yosys.js viz.js misc/yosysjs/* + rm -rf yosysjs-$(YOSYS_VER) yosysjs-$(YOSYS_VER).zip + mkdir -p yosysjs-$(YOSYS_VER) + cp viz.js misc/yosysjs/* yosys.js yosysjs-$(YOSYS_VER)/ + zip -r yosysjs-$(YOSYS_VER).zip yosysjs-$(YOSYS_VER) + +yosys.html: misc/yosys.html + $(P) cp misc/yosys.html yosys.html else ifeq ($(CONFIG),mxe) -CXX = /usr/local/src/mxe/usr/bin/i686-pc-mingw32-gcc -CXXFLAGS += -std=gnu++0x -Os -D_POSIX_SOURCE +CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc +LD = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc +CXXFLAGS += -std=c++11 -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 ifeq ($(CONFIG),icc) @@ -114,7 +177,11 @@ CXX = icpc CXXFLAGS += -std=gnu++0x -Os else ifneq ($(CONFIG),none) -$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, gcc-4.6, emcc, none) +$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, gcc-4.8, emcc, none) +endif + +ifeq ($(ENABLE_LIBYOSYS),1) +TARGETS += libyosys.so endif ifeq ($(ENABLE_READLINE),1) @@ -126,12 +193,12 @@ endif endif ifeq ($(ENABLE_PLUGINS),1) -CXXFLAGS += -DYOSYS_ENABLE_PLUGINS $(shell pkg-config --silence-errors --cflags libffi) -LDLIBS += $(shell pkg-config --silence-errors --libs libffi || echo -lffi) -ldl +CXXFLAGS += -DYOSYS_ENABLE_PLUGINS $(shell $(PKG_CONFIG) --silence-errors --cflags libffi) +LDLIBS += $(shell $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi) -ldl endif ifeq ($(ENABLE_TCL),1) -TCL_VERSION ?= tcl$(shell echo 'puts [info tclversion]' | tclsh) +TCL_VERSION ?= tcl$(shell bash -c "tclsh <(echo 'puts [info tclversion]')") TCL_INCLUDE ?= /usr/include/$(TCL_VERSION) CXXFLAGS += -I$(TCL_INCLUDE) -DYOSYS_ENABLE_TCL LDLIBS += -l$(TCL_VERSION) @@ -148,12 +215,14 @@ endif ifeq ($(ENABLE_ABC),1) CXXFLAGS += -DYOSYS_ENABLE_ABC +ifeq ($(ABCEXTERNAL),) TARGETS += yosys-abc$(EXE) endif +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 @@ -166,6 +235,13 @@ define add_share_file EXTRA_TARGETS += $(subst //,/,$(1)/$(notdir $(2))) $(subst //,/,$(1)/$(notdir $(2))): $(2) $$(P) mkdir -p $(1) + $$(Q) cp "$(YOSYS_SRC)"/$(2) $(subst //,/,$(1)/$(notdir $(2))) +endef + +define add_gen_share_file +EXTRA_TARGETS += $(subst //,/,$(1)/$(notdir $(2))) +$(subst //,/,$(1)/$(notdir $(2))): $(2) + $$(P) mkdir -p $(1) $$(Q) cp $(2) $(subst //,/,$(1)/$(notdir $(2))) endef @@ -204,10 +280,12 @@ $(eval $(call add_include_file,libs/ezsat/ezsat.h)) $(eval $(call add_include_file,libs/ezsat/ezminisat.h)) $(eval $(call add_include_file,libs/sha1/sha1.h)) $(eval $(call add_include_file,passes/fsm/fsmdata.h)) +$(eval $(call add_include_file,frontends/ast/ast.h)) $(eval $(call add_include_file,backends/ilang/ilang_backend.h)) -OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o +OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o kernel/cellaigs.o kernel/log.o: CXXFLAGS += -DYOSYS_SRC='"$(YOSYS_SRC)"' +kernel/yosys.o: CXXFLAGS += -DYOSYS_DATDIR='"$(DATDIR)"' OBJS += libs/bigint/BigIntegerAlgorithms.o libs/bigint/BigInteger.o libs/bigint/BigIntegerUtils.o OBJS += libs/bigint/BigUnsigned.o libs/bigint/BigUnsignedInABase.o @@ -226,16 +304,17 @@ OBJS += libs/minisat/SimpSolver.o OBJS += libs/minisat/Solver.o OBJS += libs/minisat/System.o -include frontends/*/Makefile.inc -include passes/*/Makefile.inc -include backends/*/Makefile.inc -include techlibs/*/Makefile.inc +include $(YOSYS_SRC)/frontends/*/Makefile.inc +include $(YOSYS_SRC)/passes/*/Makefile.inc +include $(YOSYS_SRC)/backends/*/Makefile.inc +include $(YOSYS_SRC)/techlibs/*/Makefile.inc else include frontends/verilog/Makefile.inc include frontends/ilang/Makefile.inc include frontends/ast/Makefile.inc +include frontends/blif/Makefile.inc OBJS += passes/hierarchy/hierarchy.o OBJS += passes/cmds/select.o @@ -248,7 +327,6 @@ OBJS += passes/cmds/plugin.o include passes/proc/Makefile.inc include passes/opt/Makefile.inc include passes/techmap/Makefile.inc -include passes/abc/Makefile.inc include backends/verilog/Makefile.inc include backends/ilang/Makefile.inc @@ -262,24 +340,33 @@ top-all: $(TARGETS) $(EXTRA_TARGETS) @echo " Build successful." @echo "" +ifeq ($(CONFIG),emcc) +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) $(LD) -o libyosys.so -shared -Wl,-soname,libyosys.so $(LDFLAGS) $^ $(LDLIBS) %.o: %.cc - $(P) $(CXX) -o $@ -c $(CXXFLAGS) $< + $(Q) mkdir -p $(dir $@) + $(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $< %.o: %.cpp - $(P) $(CXX) -o $@ -c $(CXXFLAGS) $< + $(Q) mkdir -p $(dir $@) + $(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $< -kernel/version_$(GIT_REV).cc: Makefile +kernel/version_$(GIT_REV).cc: $(YOSYS_SRC)/Makefile $(P) rm -f kernel/version_*.o kernel/version_*.d kernel/version_*.cc - $(Q) echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"Yosys $(YOSYS_VER) (git sha1 $(GIT_REV), $(notdir $(CXX)) ` \ + $(Q) mkdir -p kernel && echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"Yosys $(YOSYS_VER) (git sha1 $(GIT_REV), $(notdir $(CXX)) ` \ $(CXX) --version | tr ' ()' '\n' | grep '^[0-9]' | head -n1` $(filter -f% -m% -O% -DNDEBUG,$(CXXFLAGS)))\"; }" > kernel/version_$(GIT_REV).cc yosys-config: misc/yosys-config.in - $(P) $(SED) -e 's,@CXXFLAGS@,$(subst -I"$(YOSYS_SRC)",-I"$(TARGET_DATDIR)/include",$(CXXFLAGS)),;' \ - -e 's,@CXX@,$(CXX),;' -e 's,@LDFLAGS@,$(LDFLAGS),;' -e 's,@LDLIBS@,$(LDLIBS),;' \ - -e 's,@BINDIR@,$(TARGET_BINDIR),;' -e 's,@DATDIR@,$(TARGET_DATDIR),;' < misc/yosys-config.in > yosys-config + $(P) $(SED) -e 's#@CXXFLAGS@#$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(CXXFLAGS))#;' \ + -e 's#@CXX@#$(CXX)#;' -e 's#@LDFLAGS@#$(LDFLAGS)#;' -e 's#@LDLIBS@#$(LDLIBS)#;' \ + -e 's#@BINDIR@#$(BINDIR)#;' -e 's#@DATDIR@#$(DATDIR)#;' < $< > yosys-config $(Q) chmod +x yosys-config abc/abc-$(ABCREV)$(EXE): @@ -336,14 +423,24 @@ vloghtb: $(TARGETS) $(EXTRA_TARGETS) @echo "" install: $(TARGETS) $(EXTRA_TARGETS) - $(INSTALL_SUDO) mkdir -p $(DESTDIR)/bin - $(INSTALL_SUDO) install $(TARGETS) $(DESTDIR)/bin/ - $(INSTALL_SUDO) mkdir -p $(DESTDIR)/share/yosys - $(INSTALL_SUDO) cp -r share/. $(DESTDIR)/share/yosys/. + $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR) + $(INSTALL_SUDO) install $(TARGETS) $(DESTDIR)$(BINDIR) + $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(DATDIR) + $(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(DATDIR)/. +ifeq ($(ENABLE_LIBYOSYS),1) + $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR) + $(INSTALL_SUDO) ldconfig +endif uninstall: - $(INSTALL_SUDO) rm -vf $(addprefix $(DESTDIR)/bin/,$(notdir $(TARGETS))) - $(INSTALL_SUDO) rm -rvf $(DESTDIR)/share/yosys/ + $(INSTALL_SUDO) rm -vf $(addprefix $(DESTDIR)$(BINDIR),$(notdir $(TARGETS))) + $(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR) +ifeq ($(ENABLE_LIBYOSYS),1) + $(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so +endif + +update-manual: $(TARGETS) $(EXTRA_TARGETS) + cd manual && ../yosys -p 'help -write-tex-command-reference-manual' manual: $(TARGETS) $(EXTRA_TARGETS) cd manual && bash appnotes.sh @@ -352,7 +449,7 @@ manual: $(TARGETS) $(EXTRA_TARGETS) clean: rm -rf share - cd manual && bash clean.sh + if test -d manual; then cd manual && sh clean.sh; fi rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS) rm -f kernel/version_*.o kernel/version_*.cc abc/abc-[0-9a-f]* rm -f libs/*/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d techlibs/*/*.d @@ -374,7 +471,7 @@ qtcreator: vcxsrc: $(GENFILES) $(EXTRA_TARGETS) rm -rf yosys-win32-vcxsrc-$(YOSYS_VER){,.zip} set -e; for f in `ls $(filter %.cc %.cpp,$(GENFILES)) $(addsuffix .cc,$(basename $(OBJS))) $(addsuffix .cpp,$(basename $(OBJS))) 2> /dev/null`; do \ - echo "Analyse: $$f" >&2; cpp -std=gnu++0x -MM -I. -D_YOSYS_ $$f; done | sed 's,.*:,,; s,//*,/,g; s,/[^/]*/\.\./,/,g; y, \\,\n\n,;' | grep '^[^/]' | sort -u | grep -v kernel/version_ > srcfiles.txt + echo "Analyse: $$f" >&2; cpp -std=c++11 -MM -I. -D_YOSYS_ $$f; done | sed 's,.*:,,; s,//*,/,g; s,/[^/]*/\.\./,/,g; y, \\,\n\n,;' | grep '^[^/]' | sort -u | grep -v kernel/version_ > srcfiles.txt bash misc/create_vcxsrc.sh yosys-win32-vcxsrc $(YOSYS_VER) $(GIT_REV) echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"Yosys (Version Information Unavailable)\"; }" > kernel/version.cc zip yosys-win32-vcxsrc-$(YOSYS_VER)/genfiles.zip $(GENFILES) kernel/version.cc @@ -403,8 +500,8 @@ config-clang: clean config-gcc: clean echo 'CONFIG := gcc' > Makefile.conf -config-gcc-4.6: clean - echo 'CONFIG := gcc-4.6' > Makefile.conf +config-gcc-4.8: clean + echo 'CONFIG := gcc-4.8' > Makefile.conf config-emcc: clean echo 'CONFIG := emcc' > Makefile.conf @@ -440,5 +537,5 @@ echo-git-rev: -include techlibs/*/*.d .PHONY: all top-all abc test install install-abc manual clean mrproper qtcreator -.PHONY: config-clean config-clang config-gcc config-gcc-4.6 config-gprof config-sudo +.PHONY: config-clean config-clang config-gcc config-gcc-4.8 config-gprof config-sudo @@ -3,7 +3,7 @@ | | | yosys -- Yosys Open SYnthesis Suite | | | - | Copyright (C) 2012 - 2015 Clifford Wolf <clifford@clifford.at> | + | Copyright (C) 2012 - 2016 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 | @@ -56,14 +56,14 @@ For example on Ubuntu Linux 14.04 LTS the following commands will install all prerequisites for building yosys: $ yosys_deps="build-essential clang bison flex libreadline-dev gawk - tcl-dev libffi-dev git mercurial graphviz xdot pkg-config" + tcl-dev libffi-dev git mercurial graphviz xdot pkg-config python3" $ sudo apt-get install $yosys_deps There are also pre-compiled Yosys binary packages for Ubuntu and Win32 as well as a source distribution for Visual Studio. Visit the Yosys download page for more information: - http://www.clifford.at/yosys/download.html + http://www.clifford.at/yosys/download.html To configure the build system to use a specific compiler, use one of @@ -84,7 +84,7 @@ To build Yosys simply type 'make' in this directory. $ sudo make install Note that this also downloads, builds and installs ABC (using yosys-abc -as executeable name). +as executable name). Yosys can be used with the interactive command shell, with synthesis scripts or with command line arguments. Let's perform @@ -98,7 +98,7 @@ commands and "help <command>" to print details on the specified command: yosys> help help -reading the design using the verilog frontend: +reading the design using the Verilog frontend: yosys> read_verilog tests/simple/fiedler-cooley.v @@ -127,7 +127,7 @@ translating netlist to gate logic and perform some simple optimizations: yosys> techmap; opt -write design netlist to a new verilog file: +write design netlist to a new Verilog file: yosys> write_verilog synth.v @@ -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: @@ -223,7 +221,7 @@ The following Verilog-2005 features are not supported by yosys and there are currently no plans to add support for them: -- Non-sythesizable language features as defined in +- Non-synthesizable language features as defined in IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002 - The "tri", "triand", "trior", "wand" and "wor" net types @@ -257,6 +255,11 @@ Verilog Attributes and non-standard features - The "mem2reg" attribute on modules or arrays forces the early conversion of arrays to separate registers. +- The "nomeminit" attribute on modules or arrays prohibits the + creation of initialized memories. This effectively puts "mem2reg" + on all memories that are written to in an "initial" block and + are not ROMs. + - The "nolatches" attribute on modules or always-blocks prohibits the generation of logic-loops for latches. Instead all not explicitly assigned values default to x-bits. This does @@ -266,7 +269,7 @@ Verilog Attributes and non-standard features storage element. The register itself will always have all bits set to 'x' (undefined). The variable may only be used as blocking assigned temporary variable within an always block. This is mostly used internally - by yosys to synthesize verilog functions and access arrays. + by yosys to synthesize Verilog functions and access arrays. - The "onehot" attribute on wires mark them as onehot state register. This is used for example for memory port sharing and set by the fsm_map pass. @@ -274,7 +277,7 @@ Verilog Attributes and non-standard features - The "blackbox" attribute on modules is used to mark empty stub modules that have the same ports as the real thing but do not contain information on the internal configuration. This modules are only used by the synthesis - passes to identify input and output ports of cells. The verilog backend + passes to identify input and output ports of cells. The Verilog backend also does not output blackbox modules on default. - The "keep" attribute on cells and wires is used to mark objects that should @@ -283,6 +286,9 @@ Verilog Attributes and non-standard features Setting the "keep" attribute on a module has the same effect as setting it on all instances of the module. +- The "keep_hierarchy" attribute on cells and modules keeps the "flatten" + command from flattening the indicated cells and modules. + - The "init" attribute on wires is set by the frontend when a register is initialized "FPGA-style" with 'reg foo = val'. It can be used during synthesis to add the necessary reset logic. @@ -292,26 +298,35 @@ Verilog Attributes and non-standard features with "-top". Other commands, such as "flatten" and various backends use this attribute to determine the top module. +- The "src" attribute is set on cells and wires created by to the string + "<hdl-file-name>:<line-number>" by the HDL front-end and is then carried + through the synthesis. When entities are combined, a new |-separated + string is created that contains all the string from the original entities. + - In addition to the (* ... *) attribute syntax, yosys supports the non-standard {* ... *} attribute syntax to set default attributes for everything that comes after the {* ... *} statement. (Reset by adding an empty {* *} statement.) +- In module parameter and port declarations, and cell port and parameter + lists, a trailing comma is ignored. This simplifies writing verilog code + generators a bit in some cases. + - Modules can be declared with "module mod_name(...);" (with three dots instead of a list of module ports). With this syntax it is sufficient to simply declare a module port as 'input' or 'output' in the module body. -- When defining a macro with `define, all text between tripple double quotes +- When defining a macro with `define, all text between triple double quotes is interpreted as macro body, even if it contains unescaped newlines. The - tripple double quotes are removed from the macro body. For example: + tipple double quotes are removed from the macro body. For example: `define MY_MACRO(a, b) """ assign a = 23; assign b = 42; """ -- The attribute "via_celltype" can be used to implement a verilog task or +- The attribute "via_celltype" can be used to implement a Verilog task or function by instantiating the specified cell type. The value is the name of the cell type to use. For functions the name of the output port can be specified by appending it to the cell type separated by a whitespace. @@ -351,9 +366,13 @@ Verilog Attributes and non-standard features $ yosys -p 'plugin -a foo -i /lib/libm.so; read_verilog dpitest.v' - Sized constants (the syntax <size>'s?[bodh]<value>) support constant - expressions as <size>. If the expresion is not a simple identifier, it + expressions as <size>. If the expression is not a simple identifier, it must be put in parentheses. Examples: WIDTH'd42, (4+2)'b101010 +- The system tasks $finish and $display are supported in initial blocks + in and unconditional context (only if/case statements on parameters + and constant values). The intended use for this is synthesis-time DRC. + Supported features from SystemVerilog ===================================== @@ -368,3 +387,37 @@ from SystemVerilog: - The keywords "always_comb", "always_ff" and "always_latch", "logic" and "bit" are supported. + +Building the documentation +========================== + +Note that there is no need to build the manual if you just want to read it. +Simply download the PDF from http://www.clifford.at/yosys/documentation.html +instead. + +On Ubuntu, texlive needs these packages to be able to build the manual: + + sudo apt-get install texlive-binaries + sudo apt-get install texlive-science # install algorithm2e.sty + sudo apt-get install texlive-bibtex-extra # gets multibib.sty + sudo apt-get install texlive-fonts-extra # gets skull.sty and dsfont.sty + sudo apt-get install texlive-publishers # IEEEtran.cls + +Also the non-free font luximono should be installed, there is unfortulately +no Ubuntu package for this so it should be installed separately using +`getnonfreefonts`: + + wget https://tug.org/fonts/getnonfreefonts/install-getnonfreefonts + sudo texlua install-getnonfreefonts # will install to /usr/local by default, can be changed by editing BINDIR at MANDIR at the top of the script + getnonfreefonts luximono # installs to /home/user/texmf + +Then execute, from the root of the repository: + + make manual + +Notes: + +- To run `make manual` you need to have installed yosys with `make install`, + otherwise it will fail on finding `kernel/yosys.h` while building + `PRESENTATION_Prog`. + diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc index 2734ca321..93953049a 100644 --- a/backends/blif/blif.cc +++ b/backends/blif/blif.cc @@ -2,11 +2,11 @@ * 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 @@ -37,14 +37,18 @@ struct BlifDumperConfig bool conn_mode; bool impltf_mode; bool gates_mode; + bool cname_mode; bool param_mode; + bool attr_mode; bool blackbox_mode; + bool noalias_mode; std::string buf_type, buf_in, buf_out; 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), 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), noalias_mode(false) { } }; struct BlifDumper @@ -55,12 +59,35 @@ struct BlifDumper BlifDumperConfig *config; CellTypes ct; + SigMap sigmap; + dict<SigBit, int> init_bits; + BlifDumper(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BlifDumperConfig *config) : - f(f), module(module), design(design), config(config), ct(design) + f(f), module(module), design(design), config(config), ct(design), sigmap(module) { + for (Wire *wire : module->wires()) + if (wire->attributes.count("\\init")) { + SigSpec initsig = sigmap(wire); + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) + switch (initval[i]) { + case State::S0: + init_bits[initsig[i]] = 0; + break; + case State::S1: + init_bits[initsig[i]] = 1; + break; + case State::Sx: + init_bits[initsig[i]] = 2; + break; + default: + break; + } + } } - std::vector<std::string> cstr_buf; + vector<shared_str> cstr_buf; + pool<SigBit> cstr_bits_seen; const char *cstr(RTLIL::IdString id) { @@ -74,6 +101,8 @@ struct BlifDumper const char *cstr(RTLIL::SigBit sig) { + cstr_bits_seen.insert(sig); + if (sig.wire == NULL) { if (sig == RTLIL::State::S0) return config->false_type == "-" ? config->false_out.c_str() : "$false"; if (sig == RTLIL::State::S1) return config->true_type == "-" ? config->true_out.c_str() : "$true"; @@ -92,6 +121,19 @@ struct BlifDumper return cstr_buf.back().c_str(); } + const char *cstr_init(RTLIL::SigBit sig) + { + sigmap.apply(sig); + + if (init_bits.count(sig) == 0) + return ""; + + string str = stringf(" %d", init_bits.at(sig)); + + cstr_buf.push_back(str); + return cstr_buf.back().c_str(); + } + const char *subckt_or_gate(std::string cell_type) { if (!config->gates_mode) @@ -103,6 +145,26 @@ struct BlifDumper return "subckt"; } + void dump_params(const char *command, dict<IdString, Const> ¶ms) + { + for (auto ¶m : params) { + f << stringf("%s %s ", command, RTLIL::id2cstr(param.first)); + if (param.second.flags & RTLIL::CONST_FLAG_STRING) { + std::string str = param.second.decode_string(); + f << stringf("\""); + for (char ch : str) + if (ch == '"' || ch == '\\') + f << stringf("\\%c", ch); + else if (ch < 32 || ch >= 127) + f << stringf("\\%03o", ch); + else + f << stringf("%c", ch); + f << stringf("\"\n"); + } else + f << stringf("%s\n", param.second.as_string().c_str()); + } + } + void dump() { f << stringf("\n"); @@ -202,6 +264,50 @@ struct BlifDumper continue; } + if (!config->icells_mode && cell->type == "$_NAND_") { + f << stringf(".names %s %s %s\n0- 1\n-0 1\n", + cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); + continue; + } + + if (!config->icells_mode && cell->type == "$_NOR_") { + f << stringf(".names %s %s %s\n00 1\n", + cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); + continue; + } + + if (!config->icells_mode && cell->type == "$_XNOR_") { + f << stringf(".names %s %s %s\n11 1\n00 1\n", + cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); + continue; + } + + if (!config->icells_mode && cell->type == "$_AOI3_") { + f << stringf(".names %s %s %s %s\n-00 1\n0-0 1\n", + cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y"))); + continue; + } + + if (!config->icells_mode && cell->type == "$_OAI3_") { + f << stringf(".names %s %s %s %s\n00- 1\n--0 1\n", + cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y"))); + continue; + } + + if (!config->icells_mode && cell->type == "$_AOI4_") { + f << stringf(".names %s %s %s %s %s\n-0-0 1\n-00- 1\n0--0 1\n0-0- 1\n", + cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), + cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y"))); + continue; + } + + if (!config->icells_mode && cell->type == "$_OAI4_") { + f << stringf(".names %s %s %s %s %s\n00-- 1\n--00 1\n", + cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), + cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y"))); + continue; + } + if (!config->icells_mode && cell->type == "$_MUX_") { f << stringf(".names %s %s %s %s\n1-0 1\n-11 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), @@ -210,14 +316,26 @@ struct BlifDumper } if (!config->icells_mode && cell->type == "$_DFF_N_") { - f << stringf(".latch %s %s fe %s\n", - cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), cstr(cell->getPort("\\C"))); + f << stringf(".latch %s %s fe %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), + cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q"))); continue; } if (!config->icells_mode && cell->type == "$_DFF_P_") { - f << stringf(".latch %s %s re %s\n", - cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), cstr(cell->getPort("\\C"))); + f << stringf(".latch %s %s re %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), + cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q"))); + continue; + } + + if (!config->icells_mode && cell->type == "$_DLATCH_N_") { + f << stringf(".latch %s %s al %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), + cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q"))); + continue; + } + + if (!config->icells_mode && cell->type == "$_DLATCH_P_") { + f << stringf(".latch %s %s ah %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), + cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q"))); continue; } @@ -226,9 +344,8 @@ struct BlifDumper auto &inputs = cell->getPort("\\A"); auto width = cell->parameters.at("\\WIDTH").as_int(); log_assert(inputs.size() == width); - for (int i = width-1; i >= 0; i--) { + for (int i = width-1; i >= 0; i--) f << stringf(" %s", cstr(inputs.extract(i, 1))); - } auto &output = cell->getPort("\\Y"); log_assert(output.size() == 1); f << stringf(" %s", cstr(output)); @@ -255,6 +372,34 @@ struct BlifDumper continue; } + if (!config->icells_mode && cell->type == "$sop") { + f << stringf(".names"); + auto &inputs = cell->getPort("\\A"); + auto width = cell->parameters.at("\\WIDTH").as_int(); + auto depth = cell->parameters.at("\\DEPTH").as_int(); + vector<State> table = cell->parameters.at("\\TABLE").bits; + while (GetSize(table) < 2*width*depth) + table.push_back(State::S0); + log_assert(inputs.size() == width); + for (int i = 0; i < width; i++) + f << stringf(" %s", cstr(inputs.extract(i, 1))); + auto &output = cell->getPort("\\Y"); + log_assert(output.size() == 1); + f << stringf(" %s", cstr(output)); + f << stringf("\n"); + for (int i = 0; i < depth; i++) { + for (int j = 0; j < width; j++) { + bool pat0 = table.at(2*width*i + 2*j + 0) == State::S1; + bool pat1 = table.at(2*width*i + 2*j + 1) == State::S1; + if (pat0 && !pat1) f << "0"; + else if (!pat0 && pat1) f << "1"; + else f << "-"; + } + f << " 1\n"; + } + continue; + } + f << stringf(".%s %s", subckt_or_gate(cell->type.str()), cstr(cell->type)); for (auto &conn : cell->connections()) for (int i = 0; i < conn.second.size(); i++) { @@ -266,35 +411,31 @@ 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) - for (auto ¶m : cell->parameters) { - f << stringf(".param %s ", RTLIL::id2cstr(param.first)); - if (param.second.flags & RTLIL::CONST_FLAG_STRING) { - std::string str = param.second.decode_string(); - f << stringf("\""); - for (char ch : str) - if (ch == '"' || ch == '\\') - f << stringf("\\%c", ch); - else if (ch < 32 || ch >= 127) - f << stringf("\\%03o", ch); - else - f << stringf("%c", ch); - f << stringf("\"\n"); - } else - f << stringf("%s\n", param.second.as_string().c_str()); - } + dump_params(".param", cell->parameters); } for (auto &conn : module->connections()) for (int i = 0; i < conn.first.size(); i++) + { + SigBit lhs_bit = conn.first[i]; + SigBit rhs_bit = conn.second[i]; + + if (config->noalias_mode && cstr_bits_seen.count(lhs_bit) == 0) + continue; + if (config->conn_mode) - f << stringf(".conn %s %s\n", cstr(conn.second.extract(i, 1)), cstr(conn.first.extract(i, 1))); + f << stringf(".conn %s %s\n", cstr(rhs_bit), cstr(lhs_bit)); else if (!config->buf_type.empty()) - f << stringf(".%s %s %s=%s %s=%s\n", subckt_or_gate(config->buf_type), config->buf_type.c_str(), config->buf_in.c_str(), cstr(conn.second.extract(i, 1)), - config->buf_out.c_str(), cstr(conn.first.extract(i, 1))); + f << stringf(".%s %s %s=%s %s=%s\n", subckt_or_gate(config->buf_type), config->buf_type.c_str(), + config->buf_in.c_str(), cstr(rhs_bit), config->buf_out.c_str(), cstr(lhs_bit)); else - f << stringf(".names %s %s\n1 1\n", cstr(conn.second.extract(i, 1)), cstr(conn.first.extract(i, 1))); - + f << stringf(".names %s %s\n1 1\n", cstr(rhs_bit), cstr(lhs_bit)); + } f << stringf(".end\n"); } @@ -334,6 +475,11 @@ struct BlifBackend : public Backend { log(" the wire name to be used for the constant signal and no cell driving\n"); log(" that wire is generated.\n"); log("\n"); + log(" -noalias\n"); + log(" if a net name is aliasing another net name, then by default a net\n"); + log(" without fanout is created that is driven by the other net. This option\n"); + log(" suppresses the generation of this nets without fanout.\n"); + log("\n"); log("The following options can be useful when the generated file is not going to be\n"); log("read by a BLIF parser but a custom tool. It is recommended to not name the output\n"); log("file *.blif when any of this options is used.\n"); @@ -350,8 +496,14 @@ struct BlifBackend : public Backend { log(" do not generate buffers for connected wires. instead use the\n"); log(" non-standard .conn statement.\n"); log("\n"); + log(" -attr\n"); + log(" use the non-standard .attr statement to write cell attributes\n"); + log("\n"); log(" -param\n"); - log(" use the non-standard .param statement to write module parameters\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"); @@ -368,7 +520,7 @@ struct BlifBackend : public Backend { std::string false_type, false_out; BlifDumperConfig config; - log_header("Executing BLIF backend.\n"); + log_header(design, "Executing BLIF backend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -417,10 +569,18 @@ 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; } + if (args[argidx] == "-attr") { + config.attr_mode = true; + continue; + } if (args[argidx] == "-blackbox") { config.blackbox_mode = true; continue; @@ -429,6 +589,10 @@ struct BlifBackend : public Backend { config.impltf_mode = true; continue; } + if (args[argidx] == "-noalias") { + config.noalias_mode = true; + continue; + } break; } extra_args(f, filename, args, argidx); @@ -442,6 +606,7 @@ struct BlifBackend : public Backend { std::vector<RTLIL::Module*> mod_list; + design->sort(); for (auto module_it : design->modules_) { RTLIL::Module *module = module_it.second; @@ -451,7 +616,7 @@ struct BlifBackend : public Backend { if (module->processes.size() != 0) log_error("Found unmapped processes in module %s: unmapped processes are not supported in BLIF backend!\n", RTLIL::id2cstr(module->name)); if (module->memories.size() != 0) - log_error("Found munmapped emories in module %s: unmapped memories are not supported in BLIF backend!\n", RTLIL::id2cstr(module->name)); + log_error("Found unmapped memories in module %s: unmapped memories are not supported in BLIF backend!\n", RTLIL::id2cstr(module->name)); if (module->name == RTLIL::escape_id(top_module_name)) { BlifDumper::dump(*f, module, design, config); diff --git a/backends/btor/README b/backends/btor/README index 26cb377c6..efcf0d8f5 100644 --- a/backends/btor/README +++ b/backends/btor/README @@ -3,10 +3,10 @@ This is the Yosys BTOR backend. It is developed by Ahmed Irfan <irfan@fbk.eu> - Fondazione Bruno Kessler, Trento, Italy Master git repository for the BTOR backend: -https://github.com/ahmedirfan1983/yosys/tree/btor +https://github.com/ahmedirfan1983/yosys -[[CITE]] BTOR: Bit-Precise Modelling of Word-Level Problems for Model Checking +[[CITE]] BTOR: Bit-Precise Modelling of Word-Level Problems for Model Checking Johannes Kepler University, Linz, Austria http://fmv.jku.at/papers/BrummayerBiereLonsing-BPR08.pdf @@ -19,5 +19,5 @@ Todos: - async resets - etc.. -- Add support for $pmux and $lut cells +- Add support for $lut cells diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index c8fbf8d67..bbe90e85f 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -3,11 +3,11 @@ * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> * Copyright (C) 2014 Ahmed Irfan <irfan@fbk.eu> - * + * * 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 @@ -18,7 +18,7 @@ * */ -// [[CITE]] BTOR: Bit-Precise Modelling of Word-Level Problems for Model Checking +// [[CITE]] BTOR: Bit-Precise Modelling of Word-Level Problems for Model Checking // Johannes Kepler University, Linz, Austria // http://fmv.jku.at/papers/BrummayerBiereLonsing-BPR08.pdf @@ -70,17 +70,17 @@ struct BtorDumper CellTypes ct; SigMap sigmap; - std::map<RTLIL::IdString, std::set<WireInfo,WireInfoOrder>> inter_wire_map;//<wire, dependency list> for maping the intermediate wires that are output of some cell + std::map<RTLIL::IdString, std::set<WireInfo,WireInfoOrder>> inter_wire_map;//<wire, dependency list> for mapping the intermediate wires that are output of some cell std::map<RTLIL::IdString, int> line_ref;//mapping of ids to line_num of the btor file std::map<RTLIL::SigSpec, int> sig_ref;//mapping of sigspec to the line_num of the btor file int line_num;//last line number of btor file std::string str;//temp string for writing file - std::map<RTLIL::IdString, bool> basic_wires;//input wires and registers + std::map<RTLIL::IdString, bool> basic_wires;//input wires and registers RTLIL::IdString curr_cell; //current cell being dumped std::map<std::string, std::string> cell_type_translation, s_cell_type_translation; //RTLIL to BTOR translation - std::set<int> mem_next; //if memory (line_number) already has next - BtorDumper(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BtorDumperConfig *config) : - f(f), module(module), design(design), config(config), ct(design), sigmap(module) + std::map<int, std::set<std::pair<int,int>>> mem_next; // memory (line_number)'s set of condition and write + BtorDumper(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BtorDumperConfig *config) : + f(f), module(module), design(design), config(config), ct(design), sigmap(module) { line_num=0; str.clear(); @@ -143,7 +143,7 @@ struct BtorDumper //concat cell_type_translation["$concat"] = "concat"; - //signed cell type translation + //signed cell type translation //binary s_cell_type_translation["$modx"] = "srem"; s_cell_type_translation["$mody"] = "smod"; @@ -152,10 +152,10 @@ struct BtorDumper s_cell_type_translation["$le"] = "slte"; s_cell_type_translation["$gt"] = "sgt"; s_cell_type_translation["$ge"] = "sgte"; - + } - - std::vector<std::string> cstr_buf; + + vector<shared_str> cstr_buf; const char *cstr(const RTLIL::IdString id) { @@ -166,17 +166,17 @@ struct BtorDumper cstr_buf.push_back(str); return cstr_buf.back().c_str(); } - + int dump_wire(RTLIL::Wire* wire) { if(basic_wires[wire->name]) - { + { log("writing wire %s\n", cstr(wire->name)); auto it = line_ref.find(wire->name); if(it==std::end(line_ref)) { ++line_num; - line_ref[wire->name]=line_num; + line_ref[wire->name]=line_num; str = stringf("%d var %d %s", line_num, wire->width, cstr(wire->name)); f << stringf("%s\n", str.c_str()); return line_num; @@ -200,7 +200,7 @@ struct BtorDumper log(" -- found cell %s\n", cstr(cell_id)); RTLIL::Cell* cell = module->cells_.at(cell_id); const RTLIL::SigSpec* cell_output = get_cell_output(cell); - int cell_line = dump_cell(cell); + int cell_line = dump_cell(cell); if(dep_set.size()==1 && wire->width == cell_output->size()) { @@ -235,7 +235,7 @@ struct BtorDumper } if(dep_set.size()==0) { - log(" - checking sigmap\n"); + log(" - checking sigmap\n"); RTLIL::SigSpec s = RTLIL::SigSpec(wire); wire_line = dump_sigspec(&s, s.size()); line_ref[wire->name]=wire_line; @@ -243,16 +243,16 @@ struct BtorDumper line_ref[wire->name]=wire_line; return wire_line; } - else + else { - log(" -- already processed wire\n"); + log(" -- already processed wire\n"); return it->second; } } log_abort(); return -1; } - + int dump_memory(const RTLIL::Memory* memory) { log("writing memory %s\n", cstr(memory->name)); @@ -260,15 +260,54 @@ 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; + line_ref[memory->name]=line_num; f << stringf("%s\n", str.c_str()); return line_num; } else return it->second; } + int dump_memory_next(const RTLIL::Memory* memory) + { + auto mem_it = line_ref.find(memory->name); + 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"); + log_abort(); + } + else + { + auto acond_list_it = mem_next.find(mem_it->second); + if(acond_list_it!=std::end(mem_next)) + { + std::set<std::pair<int,int>>& cond_list = acond_list_it->second; + if(cond_list.empty()) + { + return 0; + } + auto it=cond_list.begin(); + ++line_num; + str = stringf("%d acond %d %d %d %d %d", line_num, memory->width, address_bits, it->first, it->second, mem_it->second); + f << stringf("%s\n", str.c_str()); + ++it; + for(; it!=cond_list.end(); ++it) + { + ++line_num; + str = stringf("%d acond %d %d %d %d %d", line_num, memory->width, address_bits, it->first, it->second, line_num-1); + f << stringf("%s\n", str.c_str()); + } + ++line_num; + str = stringf("%d anext %d %d %d %d", line_num, memory->width, address_bits, mem_it->second, line_num-1); + f << stringf("%s\n", str.c_str()); + return 1; + } + return 0; + } + } + int dump_const(const RTLIL::Const* data, int width, int offset) { log("writing const \n"); @@ -287,11 +326,11 @@ struct BtorDumper return line_num; } else - log("writing const error\n"); + log("writing const error\n"); log_abort(); return -1; } - + int dump_sigchunk(const RTLIL::SigChunk* chunk) { log("writing sigchunk\n"); @@ -299,21 +338,21 @@ struct BtorDumper if(chunk->wire == NULL) { RTLIL::Const data_const(chunk->data); - l=dump_const(&data_const, chunk->width, chunk->offset); + l=dump_const(&data_const, chunk->width, chunk->offset); } else { if (chunk->width == chunk->wire->width && chunk->offset == 0) l = dump_wire(chunk->wire); - else + else { int wire_line_num = dump_wire(chunk->wire); log_assert(wire_line_num>0); ++line_num; - str = stringf("%d slice %d %d %d %d;2", line_num, chunk->width, wire_line_num, + str = stringf("%d slice %d %d %d %d;2", line_num, chunk->width, wire_line_num, chunk->width + chunk->offset - 1, chunk->offset); f << stringf("%s\n", str.c_str()); - l = line_num; + l = line_num; } } return l; @@ -330,8 +369,8 @@ struct BtorDumper if (s.is_chunk()) { l = dump_sigchunk(&s.chunks().front()); - } - else + } + else { int l1, l2, w1, w2; l1 = dump_sigchunk(&s.chunks().front()); @@ -356,7 +395,7 @@ struct BtorDumper { l = it->second; } - + if (expected_width != s.size()) { log(" - changing width of sigspec\n"); @@ -383,7 +422,7 @@ struct BtorDumper log_assert(l>0); return l; } - + int dump_cell(const RTLIL::Cell* cell) { auto it = line_ref.find(cell->name); @@ -427,10 +466,10 @@ struct BtorDumper int w = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int(); int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int(); w = w>output_width ? w:output_width; //padding of w - int l = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), w); + int l = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), w); int cell_line = l; if(cell->type != "$pos") - { + { cell_line = ++line_num; bool reduced = (cell->type == "$not" || cell->type == "$neg") ? false : true; str = stringf ("%d %s %d %d", cell_line, cell_type_translation.at(cell->type.str()).c_str(), reduced?output_width:w, l); @@ -442,7 +481,7 @@ struct BtorDumper str = stringf ("%d slice %d %d %d %d;4", line_num, output_width, cell_line, output_width-1, 0); f << stringf("%s\n", str.c_str()); cell_line = line_num; - } + } line_ref[cell->name]=cell_line; } else if(cell->type == "$reduce_xnor" || cell->type == "$logic_not")//no direct translation in btor @@ -463,7 +502,7 @@ struct BtorDumper ++line_num; str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$reduce_xor").c_str(), output_width, l); f << stringf("%s\n", str.c_str()); - } + } ++line_num; str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$not").c_str(), output_width, l); f << stringf("%s\n", str.c_str()); @@ -471,7 +510,7 @@ struct BtorDumper } //binary cells else if(cell->type == "$and" || cell->type == "$or" || cell->type == "$xor" || cell->type == "$xnor" || - cell->type == "$lt" || cell->type == "$le" || cell->type == "$eq" || cell->type == "$ne" || + cell->type == "$lt" || cell->type == "$le" || cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex" || cell->type == "$ge" || cell->type == "$gt" ) { log("writing binary cell - %s\n", cstr(cell->type)); @@ -481,16 +520,16 @@ struct BtorDumper bool l1_signed = cell->parameters.at(RTLIL::IdString("\\A_SIGNED")).as_bool(); bool l2_signed YS_ATTRIBUTE(unused) = cell->parameters.at(RTLIL::IdString("\\B_SIGNED")).as_bool(); int l1_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int(); - int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int(); - + int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int(); + log_assert(l1_signed == l2_signed); - l1_width = l1_width > output_width ? l1_width : output_width; + l1_width = l1_width > output_width ? l1_width : output_width; l1_width = l1_width > l2_width ? l1_width : l2_width; l2_width = l2_width > l1_width ? l2_width : l1_width; int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), l1_width); int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), l2_width); - + ++line_num; std::string op = cell_type_translation.at(cell->type.str()); if(cell->type == "$lt" || cell->type == "$le" || @@ -500,13 +539,13 @@ struct BtorDumper if(l1_signed) op = s_cell_type_translation.at(cell->type.str()); } - + str = stringf ("%d %s %d %d %d", line_num, op.c_str(), output_width, l1, l2); f << stringf("%s\n", str.c_str()); line_ref[cell->name]=line_num; } - else if(cell->type == "$add" || cell->type == "$sub" || cell->type == "$mul" || cell->type == "$div" || + else if(cell->type == "$add" || cell->type == "$sub" || cell->type == "$mul" || cell->type == "$div" || cell->type == "$mod" ) { //TODO: division by zero case @@ -515,16 +554,16 @@ 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(); - int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int(); - + int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int(); + log_assert(l1_signed == l2_signed); - l1_width = l1_width > output_width ? l1_width : output_width; + l1_width = l1_width > output_width ? l1_width : output_width; l1_width = l1_width > l2_width ? l1_width : l2_width; l2_width = l2_width > l1_width ? l2_width : l1_width; int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), l1_width); int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), l2_width); - + ++line_num; std::string op = cell_type_translation.at(cell->type.str()); if(cell->type == "$div" && l1_signed) @@ -554,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))); - int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int(); - //log_assert(l2_width <= 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_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); @@ -592,7 +631,7 @@ struct BtorDumper f << stringf("%s\n", str.c_str()); cell_output = line_num; } - line_ref[cell->name] = cell_output; + line_ref[cell->name] = cell_output; } else if(cell->type == "$logic_and" || cell->type == "$logic_or")//no direct translation in btor { @@ -602,7 +641,7 @@ struct BtorDumper int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), output_width); int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), output_width); int l1_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int(); - int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int(); + int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int(); if(l1_width >1) { ++line_num; @@ -639,7 +678,7 @@ struct BtorDumper int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), output_width); int s = dump_sigspec(&cell->getPort(RTLIL::IdString("\\S")), 1); ++line_num; - str = stringf ("%d %s %d %d %d %d", + str = stringf ("%d %s %d %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), output_width, s, l2, l1); //if s is 0 then l1, if s is 1 then l2 //according to the implementation of mux cell f << stringf("%s\n", str.c_str()); @@ -654,7 +693,7 @@ struct BtorDumper int cases = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), output_width*select_width); int select = dump_sigspec(&cell->getPort(RTLIL::IdString("\\S")), select_width); int *c = new int[select_width]; - + for (int i=0; i<select_width; ++i) { ++line_num; @@ -662,15 +701,15 @@ struct BtorDumper f << stringf("%s\n", str.c_str()); c[i] = line_num; ++line_num; - str = stringf ("%d slice %d %d %d %d", line_num, output_width, cases, i*output_width+output_width-1, + str = stringf ("%d slice %d %d %d %d", line_num, output_width, cases, i*output_width+output_width-1, i*output_width); f << stringf("%s\n", str.c_str()); } - + ++line_num; str = stringf ("%d cond %d %d %d %d", line_num, output_width, c[select_width-1], c[select_width-1]+1, default_case); f << stringf("%s\n", str.c_str()); - + for (int i=select_width-2; i>=0; --i) { ++line_num; @@ -683,7 +722,7 @@ struct BtorDumper //registers else if(cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffsr") { - //TODO: remodelling fo adff cells + //TODO: remodelling of adff cells log("writing cell - %s\n", cstr(cell->type)); int output_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int(); log(" - width is %d\n", output_width); @@ -702,7 +741,7 @@ struct BtorDumper { start_bit+=output_width; slice = ++line_num; - str = stringf ("%d slice %d %d %d %d;", line_num, output_width, value, start_bit-1, + str = stringf ("%d slice %d %d %d %d;", line_num, output_width, value, start_bit-1, start_bit-output_width); f << stringf("%s\n", str.c_str()); } @@ -714,16 +753,16 @@ struct BtorDumper output_width); bool sync_reset_value_pol = cell->parameters.at(RTLIL::IdString("\\SET_POLARITY")).as_bool(); ++line_num; - str = stringf ("%d %s %d %s%d %s%d %d", line_num, cell_type_translation.at("$mux").c_str(), - output_width, sync_reset_pol ? "":"-", sync_reset, sync_reset_value_pol? "":"-", + str = stringf ("%d %s %d %s%d %s%d %d", line_num, cell_type_translation.at("$mux").c_str(), + output_width, sync_reset_pol ? "":"-", sync_reset, sync_reset_value_pol? "":"-", sync_reset_value, slice); f << stringf("%s\n", str.c_str()); slice = line_num; } ++line_num; - str = stringf ("%d %s %d %s%d %d %d", line_num, cell_type_translation.at("$mux").c_str(), + str = stringf ("%d %s %d %s%d %d %d", line_num, cell_type_translation.at("$mux").c_str(), output_width, polarity?"":"-", cond, slice, reg); - + f << stringf("%s\n", str.c_str()); int next = line_num; if(cell->type == "$adff") @@ -733,12 +772,12 @@ struct BtorDumper int async_reset_value = dump_const(&cell->parameters.at(RTLIL::IdString("\\ARST_VALUE")), output_width, 0); ++line_num; - str = stringf ("%d %s %d %s%d %d %d", line_num, cell_type_translation.at("$mux").c_str(), + str = stringf ("%d %s %d %s%d %d %d", line_num, cell_type_translation.at("$mux").c_str(), output_width, async_reset_pol ? "":"-", async_reset, async_reset_value, next); f << stringf("%s\n", str.c_str()); } ++line_num; - str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), + str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), output_width, reg, next); f << stringf("%s\n", str.c_str()); } @@ -756,7 +795,7 @@ struct BtorDumper int address = dump_sigspec(&cell->getPort(RTLIL::IdString("\\ADDR")), address_width); int data_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int(); ++line_num; - str = stringf("%d read %d %d %d", line_num, data_width, mem, address); + str = stringf("%d read %d %d %d", line_num, data_width, mem, address); f << stringf("%s\n", str.c_str()); line_ref[cell->name]=line_num; } @@ -775,43 +814,46 @@ struct BtorDumper str = cell->parameters.at(RTLIL::IdString("\\MEMID")).decode_string(); int mem = dump_memory(module->memories.at(RTLIL::IdString(str.c_str()))); //check if the memory has already next - auto it = mem_next.find(mem); + /* + auto it = mem_next.find(mem); if(it != std::end(mem_next)) { ++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; - str = stringf("%d eq 1 %d %d", line_num, mem, line_num - 1); + str = stringf("%d eq 1 %d %d; mem invar", line_num, mem, line_num - 1); f << stringf("%s\n", str.c_str()); mem = line_num - 1; - } - ++line_num; + } + */ + ++line_num; if(polarity) str = stringf("%d one 1", line_num); else str = stringf("%d zero 1", line_num); f << stringf("%s\n", str.c_str()); ++line_num; - str = stringf("%d eq 1 %d %d", line_num, clk, line_num-1); + str = stringf("%d eq 1 %d %d", line_num, clk, line_num-1); f << stringf("%s\n", str.c_str()); ++line_num; - str = stringf("%d and 1 %d %d", line_num, line_num-1, enable); + str = stringf("%d and 1 %d %d", line_num, line_num-1, enable); f << stringf("%s\n", str.c_str()); ++line_num; - str = stringf("%d write %d %d %d %d %d", line_num, data_width, address_width, mem, address, data); + str = stringf("%d write %d %d %d %d %d", line_num, data_width, address_width, mem, address, data); f << stringf("%s\n", str.c_str()); + /* ++line_num; - str = stringf("%d acond %d %d %d %d %d", line_num, data_width, address_width, line_num-2/*enable*/, line_num-1, mem); - f << stringf("%s\n", str.c_str()); + str = stringf("%d acond %d %d %d %d %d", line_num, data_width, address_width, line_num-2, line_num-1, mem); + f << stringf("%s\n", str.c_str()); ++line_num; - str = stringf("%d anext %d %d %d %d", line_num, data_width, address_width, mem, line_num-1); - f << stringf("%s\n", str.c_str()); - mem_next.insert(mem); - line_ref[cell->name]=line_num; + str = stringf("%d anext %d %d %d %d", line_num, data_width, address_width, mem, line_num-1); + f << stringf("%s\n", str.c_str()); + */ + mem_next[mem].insert(std::make_pair(line_num-1, line_num)); } else if(cell->type == "$slice") { @@ -823,11 +865,11 @@ struct BtorDumper const RTLIL::SigSpec* output YS_ATTRIBUTE(unused) = &cell->getPort(RTLIL::IdString("\\Y")); int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int(); log_assert(output->size() == output_width); - int offset = cell->parameters.at(RTLIL::IdString("\\OFFSET")).as_int(); + int offset = cell->parameters.at(RTLIL::IdString("\\OFFSET")).as_int(); ++line_num; str = stringf("%d %s %d %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), output_width, input_line, output_width+offset-1, offset); - f << stringf("%s\n", str.c_str()); - line_ref[cell->name]=line_num; + f << stringf("%s\n", str.c_str()); + line_ref[cell->name]=line_num; } else if(cell->type == "$concat") { @@ -841,10 +883,10 @@ struct BtorDumper log_assert(input_b->size() == input_b_width); int input_b_line = dump_sigspec(input_b, input_b_width); ++line_num; - str = stringf("%d %s %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), input_a_width+input_b_width, - input_a_line, input_b_line); - f << stringf("%s\n", str.c_str()); - line_ref[cell->name]=line_num; + str = stringf("%d %s %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), input_a_width+input_b_width, + input_a_line, input_b_line); + f << stringf("%s\n", str.c_str()); + line_ref[cell->name]=line_num; } curr_cell.clear(); return line_num; @@ -870,7 +912,7 @@ struct BtorDumper { output_sig = &cell->getPort(RTLIL::IdString("\\Q")); } - else + else { output_sig = &cell->getPort(RTLIL::IdString("\\Y")); } @@ -888,7 +930,7 @@ struct BtorDumper void dump() { f << stringf(";module %s\n", cstr(module->name)); - + log("creating intermediate wires map\n"); //creating map of intermediate wires as output of some cell for (auto it = module->cells_.begin(); it != module->cells_.end(); ++it) @@ -924,7 +966,7 @@ struct BtorDumper basic_wires[wire_id] = true; } } - else + else { for(unsigned i=0; i<output_sig->chunks().size(); ++i) { @@ -934,11 +976,11 @@ struct BtorDumper } } } - + log("writing input\n"); std::map<int, RTLIL::Wire*> inputs, outputs; std::vector<RTLIL::Wire*> safety; - + for (auto &wire_it : module->wires_) { RTLIL::Wire *wire = wire_it.second; if (wire->port_input) @@ -956,7 +998,7 @@ struct BtorDumper dump_wire(wire); } f << stringf("\n"); - + log("writing memories\n"); for(auto mem_it = module->memories.begin(); mem_it != module->memories.end(); ++mem_it) { @@ -972,14 +1014,20 @@ struct BtorDumper log("writing cells\n"); for(auto cell_it = module->cells_.begin(); cell_it != module->cells_.end(); ++cell_it) { - dump_cell(cell_it->second); + dump_cell(cell_it->second); } - + + log("writing memory next"); + for(auto mem_it = module->memories.begin(); mem_it != module->memories.end(); ++mem_it) + { + dump_memory_next(mem_it->second); + } + for(auto it: safety) dump_property(it); f << stringf("\n"); - + log("writing outputs info\n"); f << stringf(";outputs\n"); for (auto &it : outputs) { @@ -999,7 +1047,7 @@ struct BtorDumper struct BtorBackend : public Backend { BtorBackend() : Backend("btor", "write design to BTOR file") { } - + virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| @@ -1017,11 +1065,11 @@ struct BtorBackend : public Backend { std::string false_type, false_out; BtorDumperConfig config; - log_header("Executing BTOR backend.\n"); + log_header(design, "Executing BTOR backend.\n"); size_t argidx=1; extra_args(f, filename, args, argidx); - + if (top_module_name.empty()) for (auto & mod_it:design->modules_) if (mod_it.second->get_bool_attribute("\\top")) @@ -1031,7 +1079,7 @@ struct BtorBackend : public Backend { *f << stringf("; %s developed and maintained by Clifford Wolf <clifford@clifford.at>\n", yosys_version_str); *f << stringf("; BTOR Backend developed by Ahmed Irfan <irfan@fbk.eu> - Fondazione Bruno Kessler, Trento, Italy\n"); *f << stringf(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"); - + std::vector<RTLIL::Module*> mod_list; for (auto module_it : design->modules_) diff --git a/backends/btor/btor.ys b/backends/btor/btor.ys deleted file mode 100644 index ec28245d3..000000000 --- a/backends/btor/btor.ys +++ /dev/null @@ -1,18 +0,0 @@ -proc; -opt; opt_const -mux_undef; opt; -rename -hide;;; -#converting pmux to mux -techmap -share_map pmux2mux.v;; -#explicit type conversion -splice; opt; -#extracting memories; -memory_dff -wr_only; memory_collect;; -#flatten design -flatten;; -#converting asyn memory write to syn memory -memory_unpack; -#cell output to be a single wire -splitnets -driver; -setundef -zero -undriven; -opt;;; - diff --git a/backends/btor/verilog2btor.sh b/backends/btor/verilog2btor.sh index ab45b4901..dfd7f1a85 100755 --- a/backends/btor/verilog2btor.sh +++ b/backends/btor/verilog2btor.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Script to writing btor from verilog design +# Script to write BTOR from Verilog design # if [ "$#" -ne 3 ]; then @@ -17,19 +17,19 @@ FULL_PATH=$(readlink -f $1) DIR=$(dirname $FULL_PATH) ./yosys -q -p " -read_verilog -sv $1; -hierarchy -top $3; -hierarchy -libdir $DIR; -hierarchy -check; -proc; -opt; opt_const -mux_undef; opt; +read_verilog -sv $1; +hierarchy -top $3; +hierarchy -libdir $DIR; +hierarchy -check; +proc; +opt; opt_expr -mux_undef; opt; rename -hide;;; -#techmap -share_map pmux2mux.v;; +#techmap -map +/pmux2mux.v;; splice; opt; memory_dff -wr_only; memory_collect;; flatten;; -memory_unpack; +memory_unpack; splitnets -driver; setundef -zero -undriven; opt;;; diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc index b089be143..d16f18316 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -2,11 +2,11 @@ * 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 @@ -100,6 +100,11 @@ struct EdifBackend : public Backend { log(" -top top_module\n"); log(" set the specified module as design top module\n"); log("\n"); + log(" -nogndvcc\n"); + log(" do not create \"GND\" and \"VCC\" cells. (this will produce an error\n"); + log(" if the design contains constant nets. use \"hilomap\" to map to custom\n"); + log(" constant drivers first)\n"); + log("\n"); log("Unfortunately there are different \"flavors\" of the EDIF file format. This\n"); log("command generates EDIF files for the Xilinx place&route tools. It might be\n"); log("necessary to make small modifications to this command when a different tool\n"); @@ -108,10 +113,11 @@ struct EdifBackend : public Backend { } virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing EDIF backend.\n"); + log_header(design, "Executing EDIF backend.\n"); std::string top_module_name; std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports; + bool nogndvcc = false; CellTypes ct(design); EdifNames edif_names; @@ -122,6 +128,10 @@ struct EdifBackend : public Backend { top_module_name = args[++argidx]; continue; } + if (args[argidx] == "-nogndvcc") { + nogndvcc = true; + continue; + } break; } extra_args(f, filename, args, argidx); @@ -143,7 +153,7 @@ struct EdifBackend : public Backend { if (module->processes.size() != 0) log_error("Found unmapped processes in module %s: unmapped processes are not supported in EDIF backend!\n", RTLIL::id2cstr(module->name)); if (module->memories.size() != 0) - log_error("Found munmapped emories in module %s: unmapped memories are not supported in EDIF backend!\n", RTLIL::id2cstr(module->name)); + log_error("Found unmapped memories in module %s: unmapped memories are not supported in EDIF backend!\n", RTLIL::id2cstr(module->name)); for (auto cell_it : module->cells_) { @@ -169,21 +179,24 @@ struct EdifBackend : public Backend { *f << stringf(" (edifLevel 0)\n"); *f << stringf(" (technology (numberDefinition))\n"); - *f << stringf(" (cell GND\n"); - *f << stringf(" (cellType GENERIC)\n"); - *f << stringf(" (view VIEW_NETLIST\n"); - *f << stringf(" (viewType NETLIST)\n"); - *f << stringf(" (interface (port G (direction OUTPUT)))\n"); - *f << stringf(" )\n"); - *f << stringf(" )\n"); - - *f << stringf(" (cell VCC\n"); - *f << stringf(" (cellType GENERIC)\n"); - *f << stringf(" (view VIEW_NETLIST\n"); - *f << stringf(" (viewType NETLIST)\n"); - *f << stringf(" (interface (port P (direction OUTPUT)))\n"); - *f << stringf(" )\n"); - *f << stringf(" )\n"); + if (!nogndvcc) + { + *f << stringf(" (cell GND\n"); + *f << stringf(" (cellType GENERIC)\n"); + *f << stringf(" (view VIEW_NETLIST\n"); + *f << stringf(" (viewType NETLIST)\n"); + *f << stringf(" (interface (port G (direction OUTPUT)))\n"); + *f << stringf(" )\n"); + *f << stringf(" )\n"); + + *f << stringf(" (cell VCC\n"); + *f << stringf(" (cellType GENERIC)\n"); + *f << stringf(" (view VIEW_NETLIST\n"); + *f << stringf(" (viewType NETLIST)\n"); + *f << stringf(" (interface (port P (direction OUTPUT)))\n"); + *f << stringf(" )\n"); + *f << stringf(" )\n"); + } for (auto &cell_it : lib_cell_ports) { *f << stringf(" (cell %s\n", EDIF_DEF(cell_it.first)); @@ -279,8 +292,10 @@ struct EdifBackend : public Backend { } *f << stringf(" )\n"); *f << stringf(" (contents\n"); - *f << stringf(" (instance GND (viewRef VIEW_NETLIST (cellRef GND (libraryRef LIB))))\n"); - *f << stringf(" (instance VCC (viewRef VIEW_NETLIST (cellRef VCC (libraryRef LIB))))\n"); + if (!nogndvcc) { + *f << stringf(" (instance GND (viewRef VIEW_NETLIST (cellRef GND (libraryRef LIB))))\n"); + *f << stringf(" (instance VCC (viewRef VIEW_NETLIST (cellRef VCC (libraryRef LIB))))\n"); + } for (auto &cell_it : module->cells_) { RTLIL::Cell *cell = cell_it.second; *f << stringf(" (instance %s\n", EDIF_DEF(cell->name)); @@ -326,6 +341,8 @@ struct EdifBackend : public Backend { for (auto &ref : it.second) *f << stringf(" %s\n", ref.c_str()); if (sig.wire == NULL) { + if (nogndvcc) + log_error("Design contains constant nodes (map with \"hilomap\" first).\n"); if (sig == RTLIL::State::S0) *f << stringf(" (portRef G (instanceRef GND))\n"); if (sig == RTLIL::State::S1) diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc index 814d3e8fe..03e29c524 100644 --- a/backends/ilang/ilang_backend.cc +++ b/backends/ilang/ilang_backend.cc @@ -2,11 +2,11 @@ * 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 @@ -391,7 +391,7 @@ struct IlangBackend : public Backend { { bool selected = false; - log_header("Executing ILANG backend.\n"); + log_header(design, "Executing ILANG backend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -492,5 +492,5 @@ struct DumpPass : public Pass { } } } DumpPass; - + PRIVATE_NAMESPACE_END diff --git a/backends/ilang/ilang_backend.h b/backends/ilang/ilang_backend.h index 159cd7192..97dcbb628 100644 --- a/backends/ilang/ilang_backend.h +++ b/backends/ilang/ilang_backend.h @@ -2,11 +2,11 @@ * 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 diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc index 6d4731e73..34cb52fb4 100644 --- a/backends/intersynth/intersynth.cc +++ b/backends/intersynth/intersynth.cc @@ -2,11 +2,11 @@ * 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 @@ -73,7 +73,7 @@ struct IntersynthBackend : public Backend { } virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing INTERSYNTH backend.\n"); + log_header(design, "Executing INTERSYNTH backend.\n"); log_push(); std::vector<std::string> libfiles; @@ -113,7 +113,7 @@ struct IntersynthBackend : public Backend { } if (libs.size() > 0) - log_header("Continuing INTERSYNTH backend.\n"); + log_header(design, "Continuing INTERSYNTH backend.\n"); std::set<std::string> conntypes_code, celltypes_code; std::string netlists_code; @@ -159,7 +159,7 @@ struct IntersynthBackend : public Backend { } } - // Submodules: "std::set<string> celltypes_code" prevents duplicate cell types + // Submodules: "std::set<string> celltypes_code" prevents duplicate cell types for (auto cell_it : module->cells_) { RTLIL::Cell *cell = cell_it.second; diff --git a/backends/json/Makefile.inc b/backends/json/Makefile.inc new file mode 100644 index 000000000..a463daf91 --- /dev/null +++ b/backends/json/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += backends/json/json.o + diff --git a/backends/json/json.cc b/backends/json/json.cc new file mode 100644 index 000000000..05530ee69 --- /dev/null +++ b/backends/json/json.cc @@ -0,0 +1,538 @@ +/* + * 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/rtlil.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/celltypes.h" +#include "kernel/cellaigs.h" +#include "kernel/log.h" +#include <string> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct JsonWriter +{ + std::ostream &f; + bool use_selection; + bool aig_mode; + + Design *design; + Module *module; + + SigMap sigmap; + int sigidcounter; + dict<SigBit, string> sigids; + pool<Aig> aig_models; + + JsonWriter(std::ostream &f, bool use_selection, bool aig_mode) : + f(f), use_selection(use_selection), aig_mode(aig_mode) { } + + string get_string(string str) + { + string newstr = "\""; + for (char c : str) { + if (c == '\\') + newstr += c; + newstr += c; + } + return newstr + "\""; + } + + string get_name(IdString name) + { + return get_string(RTLIL::unescape_id(name)); + } + + string get_bits(SigSpec sig) + { + bool first = true; + string str = "["; + for (auto bit : sigmap(sig)) { + str += first ? " " : ", "; + first = false; + if (sigids.count(bit) == 0) { + string &s = sigids[bit]; + if (bit.wire == nullptr) { + if (bit == State::S0) s = "\"0\""; + else if (bit == State::S1) s = "\"1\""; + else if (bit == State::Sz) s = "\"z\""; + else s = "\"x\""; + } else + s = stringf("%d", sigidcounter++); + } + str += sigids[bit]; + } + return str + " ]"; + } + + void write_parameters(const dict<IdString, Const> ¶meters) + { + bool first = true; + for (auto ¶m : parameters) { + f << stringf("%s\n", first ? "" : ","); + f << stringf(" %s: ", get_name(param.first).c_str()); + if ((param.second.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) != 0) + f << get_string(param.second.decode_string()); + else if (GetSize(param.second.bits) > 32) + f << get_string(param.second.as_string()); + else + f << stringf("%d", param.second.as_int()); + first = false; + } + } + + void write_module(Module *module_) + { + module = module_; + log_assert(module->design == design); + sigmap.set(module); + sigids.clear(); + + // reserve 0 and 1 to avoid confusion with "0" and "1" + sigidcounter = 2; + + f << stringf(" %s: {\n", get_name(module->name).c_str()); + + f << stringf(" \"ports\": {"); + bool first = true; + for (auto n : module->ports) { + Wire *w = module->wire(n); + if (use_selection && !module->selected(w)) + continue; + f << stringf("%s\n", first ? "" : ","); + f << stringf(" %s: {\n", get_name(n).c_str()); + f << stringf(" \"direction\": \"%s\",\n", w->port_input ? w->port_output ? "inout" : "input" : "output"); + f << stringf(" \"bits\": %s\n", get_bits(w).c_str()); + f << stringf(" }"); + first = false; + } + f << stringf("\n },\n"); + + f << stringf(" \"cells\": {"); + first = true; + for (auto c : module->cells()) { + if (use_selection && !module->selected(c)) + continue; + f << stringf("%s\n", first ? "" : ","); + f << stringf(" %s: {\n", get_name(c->name).c_str()); + f << stringf(" \"hide_name\": %s,\n", c->name[0] == '$' ? "1" : "0"); + f << stringf(" \"type\": %s,\n", get_name(c->type).c_str()); + if (aig_mode) { + Aig aig(c); + if (!aig.name.empty()) { + f << stringf(" \"model\": \"%s\",\n", aig.name.c_str()); + aig_models.insert(aig); + } + } + f << stringf(" \"parameters\": {"); + write_parameters(c->parameters); + f << stringf("\n },\n"); + f << stringf(" \"attributes\": {"); + write_parameters(c->attributes); + f << stringf("\n },\n"); + if (c->known()) { + f << stringf(" \"port_directions\": {"); + bool first2 = true; + for (auto &conn : c->connections()) { + string direction = "output"; + if (c->input(conn.first)) + direction = c->output(conn.first) ? "inout" : "input"; + f << stringf("%s\n", first2 ? "" : ","); + f << stringf(" %s: \"%s\"", get_name(conn.first).c_str(), direction.c_str()); + first2 = false; + } + f << stringf("\n },\n"); + } + f << stringf(" \"connections\": {"); + bool first2 = true; + for (auto &conn : c->connections()) { + f << stringf("%s\n", first2 ? "" : ","); + f << stringf(" %s: %s", get_name(conn.first).c_str(), get_bits(conn.second).c_str()); + first2 = false; + } + f << stringf("\n }\n"); + f << stringf(" }"); + first = false; + } + f << stringf("\n },\n"); + + f << stringf(" \"netnames\": {"); + first = true; + for (auto w : module->wires()) { + if (use_selection && !module->selected(w)) + continue; + f << stringf("%s\n", first ? "" : ","); + f << stringf(" %s: {\n", get_name(w->name).c_str()); + f << stringf(" \"hide_name\": %s,\n", w->name[0] == '$' ? "1" : "0"); + f << stringf(" \"bits\": %s,\n", get_bits(w).c_str()); + f << stringf(" \"attributes\": {"); + write_parameters(w->attributes); + f << stringf("\n }\n"); + f << stringf(" }"); + first = false; + } + f << stringf("\n }\n"); + + f << stringf(" }"); + } + + void write_design(Design *design_) + { + design = design_; + f << stringf("{\n"); + f << stringf(" \"creator\": %s,\n", get_string(yosys_version_str).c_str()); + f << stringf(" \"modules\": {\n"); + vector<Module*> modules = use_selection ? design->selected_modules() : design->modules(); + bool first_module = true; + for (auto mod : modules) { + if (!first_module) + f << stringf(",\n"); + write_module(mod); + first_module = false; + } + f << stringf("\n }"); + if (!aig_models.empty()) { + f << stringf(",\n \"models\": {\n"); + bool first_model = true; + for (auto &aig : aig_models) { + if (!first_model) + f << stringf(",\n"); + f << stringf(" \"%s\": [\n", aig.name.c_str()); + int node_idx = 0; + for (auto &node : aig.nodes) { + if (node_idx != 0) + f << stringf(",\n"); + f << stringf(" /* %3d */ [ ", node_idx); + if (node.portbit >= 0) + f << stringf("\"%sport\", \"%s\", %d", node.inverter ? "n" : "", + log_id(node.portname), node.portbit); + else if (node.left_parent < 0 && node.right_parent < 0) + f << stringf("\"%s\"", node.inverter ? "true" : "false"); + else + f << stringf("\"%s\", %d, %d", node.inverter ? "nand" : "and", node.left_parent, node.right_parent); + for (auto &op : node.outports) + f << stringf(", \"%s\", %d", log_id(op.first), op.second); + f << stringf(" ]"); + node_idx++; + } + f << stringf("\n ]"); + first_model = false; + } + f << stringf("\n }"); + } + f << stringf("\n}\n"); + } +}; + +struct JsonBackend : public Backend { + JsonBackend() : Backend("json", "write design to a JSON file") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_json [options] [filename]\n"); + log("\n"); + log("Write a JSON netlist of the current design.\n"); + log("\n"); + log(" -aig\n"); + log(" include AIG models for the different gate types\n"); + log("\n"); + log("\n"); + log("The general syntax of the JSON output created by this command is as follows:\n"); + log("\n"); + log(" {\n"); + log(" \"modules\": {\n"); + log(" <module_name>: {\n"); + log(" \"ports\": {\n"); + log(" <port_name>: <port_details>,\n"); + log(" ...\n"); + log(" },\n"); + log(" \"cells\": {\n"); + log(" <cell_name>: <cell_details>,\n"); + log(" ...\n"); + log(" },\n"); + log(" \"netnames\": {\n"); + log(" <net_name>: <net_details>,\n"); + log(" ...\n"); + log(" }\n"); + log(" }\n"); + log(" },\n"); + log(" \"models\": {\n"); + log(" ...\n"); + log(" },\n"); + log(" }\n"); + log("\n"); + log("Where <port_details> is:\n"); + log("\n"); + log(" {\n"); + log(" \"direction\": <\"input\" | \"output\" | \"inout\">,\n"); + log(" \"bits\": <bit_vector>\n"); + log(" }\n"); + log("\n"); + log("And <cell_details> is:\n"); + log("\n"); + log(" {\n"); + log(" \"hide_name\": <1 | 0>,\n"); + log(" \"type\": <cell_type>,\n"); + log(" \"parameters\": {\n"); + log(" <parameter_name>: <parameter_value>,\n"); + log(" ...\n"); + log(" },\n"); + log(" \"attributes\": {\n"); + log(" <attribute_name>: <attribute_value>,\n"); + log(" ...\n"); + log(" },\n"); + log(" \"port_directions\": {\n"); + log(" <port_name>: <\"input\" | \"output\" | \"inout\">,\n"); + log(" ...\n"); + log(" },\n"); + log(" \"connections\": {\n"); + log(" <port_name>: <bit_vector>,\n"); + log(" ...\n"); + log(" },\n"); + log(" }\n"); + log("\n"); + log("And <net_details> is:\n"); + log("\n"); + log(" {\n"); + log(" \"hide_name\": <1 | 0>,\n"); + log(" \"bits\": <bit_vector>\n"); + log(" }\n"); + log("\n"); + log("The \"hide_name\" fields are set to 1 when the name of this cell or net is\n"); + log("automatically created and is likely not of interest for a regular user.\n"); + log("\n"); + log("The \"port_directions\" section is only included for cells for which the\n"); + log("interface is known.\n"); + log("\n"); + log("Module and cell ports and nets can be single bit wide or vectors of multiple\n"); + log("bits. Each individual signal bit is assigned a unique integer. The <bit_vector>\n"); + log("values referenced above are vectors of this integers. Signal bits that are\n"); + log("connected to a constant driver are denoted as string \"0\" or \"1\" instead of\n"); + log("a number.\n"); + log("\n"); + log("For example the following Verilog code:\n"); + log("\n"); + log(" module test(input x, y);\n"); + log(" (* keep *) foo #(.P(42), .Q(1337))\n"); + log(" foo_inst (.A({x, y}), .B({y, x}), .C({4'd10, {4{x}}}));\n"); + log(" endmodule\n"); + log("\n"); + log("Translates to the following JSON output:\n"); + log("\n"); + log(" {\n"); + log(" \"modules\": {\n"); + log(" \"test\": {\n"); + log(" \"ports\": {\n"); + log(" \"x\": {\n"); + log(" \"direction\": \"input\",\n"); + log(" \"bits\": [ 2 ]\n"); + log(" },\n"); + log(" \"y\": {\n"); + log(" \"direction\": \"input\",\n"); + log(" \"bits\": [ 3 ]\n"); + log(" }\n"); + log(" },\n"); + log(" \"cells\": {\n"); + log(" \"foo_inst\": {\n"); + log(" \"hide_name\": 0,\n"); + log(" \"type\": \"foo\",\n"); + log(" \"parameters\": {\n"); + log(" \"Q\": 1337,\n"); + log(" \"P\": 42\n"); + log(" },\n"); + log(" \"attributes\": {\n"); + log(" \"keep\": 1,\n"); + log(" \"src\": \"test.v:2\"\n"); + log(" },\n"); + log(" \"connections\": {\n"); + log(" \"C\": [ 2, 2, 2, 2, \"0\", \"1\", \"0\", \"1\" ],\n"); + log(" \"B\": [ 2, 3 ],\n"); + log(" \"A\": [ 3, 2 ]\n"); + log(" }\n"); + log(" }\n"); + log(" },\n"); + log(" \"netnames\": {\n"); + log(" \"y\": {\n"); + log(" \"hide_name\": 0,\n"); + log(" \"bits\": [ 3 ],\n"); + log(" \"attributes\": {\n"); + log(" \"src\": \"test.v:1\"\n"); + log(" }\n"); + log(" },\n"); + log(" \"x\": {\n"); + log(" \"hide_name\": 0,\n"); + log(" \"bits\": [ 2 ],\n"); + log(" \"attributes\": {\n"); + log(" \"src\": \"test.v:1\"\n"); + log(" }\n"); + log(" }\n"); + log(" }\n"); + log(" }\n"); + log(" }\n"); + log(" }\n"); + log("\n"); + log("The models are given as And-Inverter-Graphs (AIGs) in the following form:\n"); + log("\n"); + log(" \"models\": {\n"); + log(" <model_name>: [\n"); + log(" /* 0 */ [ <node-spec> ],\n"); + log(" /* 1 */ [ <node-spec> ],\n"); + log(" /* 2 */ [ <node-spec> ],\n"); + log(" ...\n"); + log(" ],\n"); + log(" ...\n"); + log(" },\n"); + log("\n"); + log("The following node-types may be used:\n"); + log("\n"); + log(" [ \"port\", <portname>, <bitindex>, <out-list> ]\n"); + log(" - the value of the specified input port bit\n"); + log("\n"); + log(" [ \"nport\", <portname>, <bitindex>, <out-list> ]\n"); + log(" - the inverted value of the specified input port bit\n"); + log("\n"); + log(" [ \"and\", <node-index>, <node-index>, <out-list> ]\n"); + log(" - the ANDed value of the specified nodes\n"); + log("\n"); + log(" [ \"nand\", <node-index>, <node-index>, <out-list> ]\n"); + log(" - the inverted ANDed value of the specified nodes\n"); + log("\n"); + log(" [ \"true\", <out-list> ]\n"); + log(" - the constant value 1\n"); + log("\n"); + log(" [ \"false\", <out-list> ]\n"); + log(" - the constant value 0\n"); + log("\n"); + log("All nodes appear in topological order. I.e. only nodes with smaller indices\n"); + log("are referenced by \"and\" and \"nand\" nodes.\n"); + log("\n"); + log("The optional <out-list> at the end of a node specification is a list of\n"); + log("output portname and bitindex pairs, specifying the outputs driven by this node.\n"); + log("\n"); + log("For example, the following is the model for a 3-input 3-output $reduce_and cell\n"); + log("inferred by the following code:\n"); + log("\n"); + log(" module test(input [2:0] in, output [2:0] out);\n"); + log(" assign in = &out;\n"); + log(" endmodule\n"); + log("\n"); + log(" \"$reduce_and:3U:3\": [\n"); + log(" /* 0 */ [ \"port\", \"A\", 0 ],\n"); + log(" /* 1 */ [ \"port\", \"A\", 1 ],\n"); + log(" /* 2 */ [ \"and\", 0, 1 ],\n"); + log(" /* 3 */ [ \"port\", \"A\", 2 ],\n"); + log(" /* 4 */ [ \"and\", 2, 3, \"Y\", 0 ],\n"); + log(" /* 5 */ [ \"false\", \"Y\", 1, \"Y\", 2 ]\n"); + log(" ]\n"); + log("\n"); + log("Future version of Yosys might add support for additional fields in the JSON\n"); + log("format. A program processing this format must ignore all unknown fields.\n"); + log("\n"); + } + virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + { + bool aig_mode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-aig") { + aig_mode = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + log_header(design, "Executing JSON backend.\n"); + + JsonWriter json_writer(*f, false, aig_mode); + json_writer.write_design(design); + } +} JsonBackend; + +struct JsonPass : public Pass { + JsonPass() : Pass("json", "write design in JSON format") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" json [options] [selection]\n"); + log("\n"); + log("Write a JSON netlist of all selected objects.\n"); + log("\n"); + log(" -o <filename>\n"); + log(" write to the specified file.\n"); + log("\n"); + log(" -aig\n"); + log(" also include AIG models for the different gate types\n"); + log("\n"); + log("See 'help write_json' for a description of the JSON format used.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + std::string filename; + bool aig_mode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-o" && argidx+1 < args.size()) { + filename = args[++argidx]; + continue; + } + if (args[argidx] == "-aig") { + aig_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + std::ostream *f; + std::stringstream buf; + + if (!filename.empty()) { + std::ofstream *ff = new std::ofstream; + ff->open(filename.c_str(), std::ofstream::trunc); + if (ff->fail()) { + delete ff; + log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno)); + } + f = ff; + } else { + f = &buf; + } + + JsonWriter json_writer(*f, true, aig_mode); + json_writer.write_design(design); + + if (!filename.empty()) { + delete f; + } else { + log("%s", buf.str().c_str()); + } + } +} JsonPass; + +PRIVATE_NAMESPACE_END diff --git a/backends/smt2/Makefile.inc b/backends/smt2/Makefile.inc index 4e0a393a8..eacda2734 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 000000000..b195266eb --- /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 000000000..6fccb344f --- /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 8451eff4f..e869f78cd 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -2,11 +2,11 @@ * 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 @@ -32,17 +32,21 @@ struct Smt2Worker CellTypes ct; SigMap sigmap; RTLIL::Module *module; - bool bvmode; + bool bvmode, memmode, regsmode, wiresmode, verbose; int idcounter; std::vector<std::string> decls, trans; std::map<RTLIL::SigBit, RTLIL::Cell*> bit_driver; std::set<RTLIL::Cell*> exported_cells; + pool<Cell*> recursive_cells, registers; std::map<RTLIL::SigBit, std::pair<int, int>> fcache; + std::map<Cell*, int> memarrays; std::map<int, int> bvsizes; - Smt2Worker(RTLIL::Module *module, bool bvmode) : ct(module->design), sigmap(module), module(module), bvmode(bvmode), 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))); @@ -64,6 +68,9 @@ struct Smt2Worker void register_bool(RTLIL::SigBit bit, int id) { + if (verbose) log("%*s-> register_bool: %s %d\n", 2+2*GetSize(recursive_cells), "", + log_signal(bit), id); + sigmap.apply(bit); log_assert(fcache.count(bit) == 0); fcache[bit] = std::pair<int, int>(id, -1); @@ -71,6 +78,9 @@ struct Smt2Worker void register_bv(RTLIL::SigSpec sig, int id) { + if (verbose) log("%*s-> register_bv: %s %d\n", 2+2*GetSize(recursive_cells), "", + log_signal(sig), id); + log_assert(bvmode); sigmap.apply(sig); @@ -85,6 +95,9 @@ struct Smt2Worker void register_boolvec(RTLIL::SigSpec sig, int id) { + if (verbose) log("%*s-> register_boolvec: %s %d\n", 2+2*GetSize(recursive_cells), "", + log_signal(sig), id); + log_assert(bvmode); sigmap.apply(sig); register_bool(sig[0], id); @@ -105,6 +118,8 @@ struct Smt2Worker sigmap.apply(bit); if (fcache.count(bit) == 0) { + if (verbose) log("%*s-> external bool: %s\n", 2+2*GetSize(recursive_cells), "", + log_signal(bit)); decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n", log_id(module), idcounter, log_id(module), log_signal(bit))); register_bool(bit, idcounter++); @@ -118,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") @@ -128,10 +143,14 @@ struct Smt2Worker std::vector<std::string> subexpr; - for (auto bit : sig) - if (bit_driver.count(bit)) - export_cell(bit_driver.at(bit)); - sigmap.apply(sig); + SigSpec orig_sig; + while (orig_sig != sig) { + for (auto bit : sig) + if (bit_driver.count(bit)) + export_cell(bit_driver.at(bit)); + orig_sig = sig; + sigmap.apply(sig); + } for (int i = 0, j = 1; i < GetSize(sig); i += j, j = 1) { @@ -161,9 +180,10 @@ struct Smt2Worker j++; } if (t1.second == 0 && j == bvsizes.at(t1.first)) - subexpr.push_back(stringf("(|%s#%d| state)", log_id(module), t1.first)); + subexpr.push_back(stringf("(|%s#%d| %s)", log_id(module), t1.first, state_name)); else - subexpr.push_back(stringf("((_ extract %d %d) (|%s#%d| state))", t1.second + j - 1, t1.second, log_id(module), t1.first)); + subexpr.push_back(stringf("((_ extract %d %d) (|%s#%d| %s))", + t1.second + j - 1, t1.second, log_id(module), t1.first, state_name)); continue; } @@ -171,17 +191,23 @@ struct Smt2Worker while (i+j < GetSize(sig) && sig[i+j].wire && !fcache.count(sig[i+j]) && !seen_bits.count(sig[i+j])) seen_bits.insert(sig[i+j]), j++; + if (verbose) log("%*s-> external bv: %s\n", 2+2*GetSize(recursive_cells), "", + log_signal(sig.extract(i, j))); + for (auto bit : sig.extract(i, j)) + log_assert(bit_driver.count(bit) == 0); decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n", log_id(module), idcounter, log_id(module), j, log_signal(sig.extract(i, j)))); - subexpr.push_back(stringf("(|%s#%d| state)", log_id(module), idcounter)); + subexpr.push_back(stringf("(|%s#%d| %s)", log_id(module), idcounter, state_name)); register_bv(sig.extract(i, j), idcounter++); } if (GetSize(subexpr) > 1) { - std::string expr = "(concat"; - for (int i = GetSize(subexpr)-1; i >= 0; i--) + std::string expr = "", end_str = ""; + for (int i = GetSize(subexpr)-1; i >= 0; i--) { + if (i > 0) expr += " (concat", end_str += ")"; expr += " " + subexpr[i]; - return expr + ")"; + } + return expr.substr(1) + end_str; } else { log_assert(GetSize(subexpr) == 1); return subexpr[0]; @@ -190,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) { @@ -202,10 +228,12 @@ struct Smt2Worker else processed_expr += ch; } + if (verbose) log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", + log_id(cell)); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool %s) ; %s\n", log_id(module), idcounter, log_id(module), processed_expr.c_str(), log_signal(bit))); register_bool(bit, idcounter++); - return; + recursive_cells.erase(cell); } void export_bvop(RTLIL::Cell *cell, std::string expr, char type = 0) @@ -216,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")) { @@ -243,6 +271,9 @@ struct Smt2Worker if (width != GetSize(sig_y) && type != 'b') processed_expr = stringf("((_ extract %d 0) %s)", GetSize(sig_y)-1, processed_expr.c_str()); + if (verbose) log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", + log_id(cell)); + if (type == 'b') { decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool %s) ; %s\n", log_id(module), idcounter, log_id(module), processed_expr.c_str(), log_signal(sig_y))); @@ -252,7 +283,8 @@ struct Smt2Worker log_id(module), idcounter, log_id(module), GetSize(sig_y), processed_expr.c_str(), log_signal(sig_y))); register_bv(sig_y, idcounter++); } - return; + + recursive_cells.erase(cell); } void export_reduce(RTLIL::Cell *cell, std::string expr, bool identity_val) @@ -270,23 +302,35 @@ struct Smt2Worker } else processed_expr += ch; + if (verbose) log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", + log_id(cell)); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool %s) ; %s\n", log_id(module), idcounter, log_id(module), processed_expr.c_str(), log_signal(sig_y))); register_boolvec(sig_y, idcounter++); - return; + recursive_cells.erase(cell); } void export_cell(RTLIL::Cell *cell) { + if (verbose) log("%*s=> export_cell %s (%s) [%s]\n", 2+2*GetSize(recursive_cells), "", + log_id(cell), log_id(cell->type), exported_cells.count(cell) ? "old" : "new"); + + if (recursive_cells.count(cell)) + log_error("Found logic loop in module %s! See cell %s.\n", log_id(module), log_id(cell)); + if (exported_cells.count(cell)) return; + exported_cells.insert(cell); + recursive_cells.insert(cell); if (cell->type == "$_DFF_P_" || cell->type == "$_DFF_N_") { - std::string expr_d = get_bool(cell->getPort("\\D")); - std::string expr_q = get_bool(cell->getPort("\\Q"), "next_state"); - trans.push_back(stringf(" (= %s %s)\n", expr_d.c_str(), expr_q.c_str())); + registers.insert(cell); + decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n", + log_id(module), idcounter, log_id(module), log_signal(cell->getPort("\\Q")))); + register_bool(cell->getPort("\\Q"), idcounter++); + recursive_cells.erase(cell); return; } @@ -304,94 +348,157 @@ struct Smt2Worker if (cell->type == "$_AOI4_") return export_gate(cell, "(not (or (and A B) (and C D)))"); if (cell->type == "$_OAI4_") return export_gate(cell, "(not (and (or A B) (or C D)))"); - // FIXME: $lut $assert - - if (!bvmode) - log_error("Unsupported cell type %s for cell %s.%s. (Maybe this cell type would be supported in -bv mode?)\n", - log_id(cell->type), log_id(module), log_id(cell)); + // FIXME: $lut - if (cell->type == "$dff") + if (bvmode) { - std::string expr_d = get_bv(cell->getPort("\\D")); - std::string expr_q = get_bv(cell->getPort("\\Q"), "next_state"); - trans.push_back(stringf(" (= %s %s)\n", expr_d.c_str(), expr_q.c_str())); - return; + if (cell->type == "$dff") + { + registers.insert(cell); + decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n", + log_id(module), idcounter, log_id(module), GetSize(cell->getPort("\\Q")), log_signal(cell->getPort("\\Q")))); + register_bv(cell->getPort("\\Q"), idcounter++); + recursive_cells.erase(cell); + return; + } + + if (cell->type == "$and") return export_bvop(cell, "(bvand A B)"); + if (cell->type == "$or") return export_bvop(cell, "(bvor A B)"); + if (cell->type == "$xor") return export_bvop(cell, "(bvxor A B)"); + if (cell->type == "$xnor") return export_bvop(cell, "(bvxnor A B)"); + + if (cell->type == "$shl") return export_bvop(cell, "(bvshl A B)", 's'); + if (cell->type == "$shr") return export_bvop(cell, "(bvlshr A B)", 's'); + if (cell->type == "$sshl") return export_bvop(cell, "(bvshl A B)", 's'); + if (cell->type == "$sshr") return export_bvop(cell, "(bvLshr A B)", 's'); + + // FIXME: $shift $shiftx + + if (cell->type == "$lt") return export_bvop(cell, "(bvUlt A B)", 'b'); + if (cell->type == "$le") return export_bvop(cell, "(bvUle A B)", 'b'); + if (cell->type == "$ge") return export_bvop(cell, "(bvUge A B)", 'b'); + if (cell->type == "$gt") return export_bvop(cell, "(bvUgt A B)", 'b'); + + if (cell->type == "$ne") return export_bvop(cell, "(distinct A B)", 'b'); + if (cell->type == "$nex") return export_bvop(cell, "(distinct A B)", 'b'); + if (cell->type == "$eq") return export_bvop(cell, "(= A B)", 'b'); + if (cell->type == "$eqx") return export_bvop(cell, "(= A B)", 'b'); + + if (cell->type == "$not") return export_bvop(cell, "(bvnot A)"); + if (cell->type == "$pos") return export_bvop(cell, "A"); + if (cell->type == "$neg") return export_bvop(cell, "(bvneg A)"); + + if (cell->type == "$add") return export_bvop(cell, "(bvadd A B)"); + if (cell->type == "$sub") return export_bvop(cell, "(bvsub A B)"); + if (cell->type == "$mul") return export_bvop(cell, "(bvmul A B)"); + if (cell->type == "$div") return export_bvop(cell, "(bvUdiv A B)", 'd'); + if (cell->type == "$mod") return export_bvop(cell, "(bvUrem A B)", 'd'); + + if (cell->type == "$reduce_and") return export_reduce(cell, "(and A)", true); + if (cell->type == "$reduce_or") return export_reduce(cell, "(or A)", false); + if (cell->type == "$reduce_xor") return export_reduce(cell, "(xor A)", false); + if (cell->type == "$reduce_xnor") return export_reduce(cell, "(not (xor A))", false); + if (cell->type == "$reduce_bool") return export_reduce(cell, "(or A)", false); + + if (cell->type == "$logic_not") return export_reduce(cell, "(not (or A))", false); + if (cell->type == "$logic_and") return export_reduce(cell, "(and (or A) (or B))", false); + if (cell->type == "$logic_or") return export_reduce(cell, "(or A B)", false); + + if (cell->type == "$mux" || cell->type == "$pmux") + { + int width = GetSize(cell->getPort("\\Y")); + std::string processed_expr = get_bv(cell->getPort("\\A")); + + RTLIL::SigSpec sig_b = cell->getPort("\\B"); + RTLIL::SigSpec sig_s = cell->getPort("\\S"); + get_bv(sig_b); + get_bv(sig_s); + + for (int i = 0; i < GetSize(sig_s); i++) + processed_expr = stringf("(ite %s %s %s)", get_bool(sig_s[i]).c_str(), + get_bv(sig_b.extract(i*width, width)).c_str(), processed_expr.c_str()); + + if (verbose) log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", + log_id(cell)); + RTLIL::SigSpec sig = sigmap(cell->getPort("\\Y")); + decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", + log_id(module), idcounter, log_id(module), width, processed_expr.c_str(), log_signal(sig))); + register_bv(sig, idcounter++); + recursive_cells.erase(cell); + return; + } + + // FIXME: $slice $concat } - if (cell->type == "$and") return export_bvop(cell, "(bvand A B)"); - if (cell->type == "$or") return export_bvop(cell, "(bvor A B)"); - if (cell->type == "$xor") return export_bvop(cell, "(bvxor A B)"); - if (cell->type == "$xnor") return export_bvop(cell, "(bvxnor A B)"); - - if (cell->type == "$shl") return export_bvop(cell, "(bvshl A B)", 's'); - if (cell->type == "$shr") return export_bvop(cell, "(bvlshr A B)", 's'); - if (cell->type == "$sshl") return export_bvop(cell, "(bvshl A B)", 's'); - if (cell->type == "$sshr") return export_bvop(cell, "(bvLshr A B)", 's'); - - // FIXME: $shift $shiftx - - if (cell->type == "$lt") return export_bvop(cell, "(bvUlt A B)", 'b'); - if (cell->type == "$le") return export_bvop(cell, "(bvUle A B)", 'b'); - if (cell->type == "$ge") return export_bvop(cell, "(bvUge A B)", 'b'); - if (cell->type == "$gt") return export_bvop(cell, "(bvUgt A B)", 'b'); - - if (cell->type == "$ne") return export_bvop(cell, "(distinct A B)", 'b'); - if (cell->type == "$nex") return export_bvop(cell, "(distinct A B)", 'b'); - if (cell->type == "$eq") return export_bvop(cell, "(= A B)", 'b'); - if (cell->type == "$eqx") return export_bvop(cell, "(= A B)", 'b'); - - if (cell->type == "$not") return export_bvop(cell, "(bvnot A)"); - if (cell->type == "$pos") return export_bvop(cell, "A"); - if (cell->type == "$neg") return export_bvop(cell, "(bvneg A)"); - - if (cell->type == "$add") return export_bvop(cell, "(bvadd A B)"); - if (cell->type == "$sub") return export_bvop(cell, "(bvsub A B)"); - if (cell->type == "$mul") return export_bvop(cell, "(bvmul A B)"); - if (cell->type == "$div") return export_bvop(cell, "(bvUdiv A B)", 'd'); - if (cell->type == "$mod") return export_bvop(cell, "(bvUrem A B)", 'd'); - - if (cell->type == "$reduce_and") return export_reduce(cell, "(and A)", true); - if (cell->type == "$reduce_or") return export_reduce(cell, "(or A)", false); - if (cell->type == "$reduce_xor") return export_reduce(cell, "(xor A)", false); - if (cell->type == "$reduce_xnor") return export_reduce(cell, "(not (xor A))", false); - if (cell->type == "$reduce_bool") return export_reduce(cell, "(or A)", false); - - if (cell->type == "$logic_not") return export_reduce(cell, "(not (or A))", false); - if (cell->type == "$logic_and") return export_reduce(cell, "(and (or A) (or B))", false); - if (cell->type == "$logic_or") return export_reduce(cell, "(or A B)", false); - - if (cell->type == "$mux" || cell->type == "$pmux") + if (memmode && cell->type == "$mem") { - int width = GetSize(cell->getPort("\\Y")); - std::string processed_expr = get_bv(cell->getPort("\\A")); + int arrayid = idcounter++; + memarrays[cell] = arrayid; - RTLIL::SigSpec sig_b = cell->getPort("\\B"); - RTLIL::SigSpec sig_s = cell->getPort("\\S"); - get_bv(sig_b); - get_bv(sig_s); + int abits = cell->getParam("\\ABITS").as_int(); + int width = cell->getParam("\\WIDTH").as_int(); + int rd_ports = cell->getParam("\\RD_PORTS").as_int(); - for (int i = 0; i < GetSize(sig_s); i++) - processed_expr = stringf("(ite %s %s %s)", get_bool(sig_s[i]).c_str(), - get_bv(sig_b.extract(i*width, width)).c_str(), processed_expr.c_str()); + decls.push_back(stringf("(declare-fun |%s#%d#0| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", + log_id(module), arrayid, log_id(module), abits, width, log_id(cell))); - RTLIL::SigSpec sig = sigmap(cell->getPort("\\Y")); - decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", - log_id(module), idcounter, log_id(module), width, processed_expr.c_str(), log_signal(sig))); - register_bv(sig, idcounter++); + decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s#%d#0| state))\n", + log_id(module), log_id(cell), log_id(module), abits, width, log_id(module), arrayid)); + + for (int i = 0; i < rd_ports; i++) + { + std::string addr = get_bv(cell->getPort("\\RD_ADDR").extract(abits*i, abits)); + SigSpec data_sig = cell->getPort("\\RD_DATA").extract(width*i, width); + + if (cell->getParam("\\RD_CLK_ENABLE").extract(i).as_bool()) + log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! " + "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(data_sig), log_id(cell), log_id(module)); + + decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s#%d#0| state) %s)) ; %s\n", + log_id(module), idcounter, log_id(module), width, log_id(module), arrayid, addr.c_str(), log_signal(data_sig))); + register_bv(data_sig, idcounter++); + } + + registers.insert(cell); + recursive_cells.erase(cell); return; } - // FIXME: $slice $concat - - log_error("Unsupported cell type %s for cell %s.%s.\n", + log_error("Unsupported cell type %s for cell %s.%s. (Maybe this cell type would be supported in -bv or -mem mode?)\n", log_id(cell->type), log_id(module), log_id(cell)); } void run() { - for (auto wire : module->wires()) - if (wire->port_id || wire->get_bool_attribute("\\keep")) { + if (verbose) log("=> export logic driving outputs\n"); + + pool<SigBit> reg_bits; + if (regsmode) { + for (auto cell : module->cells()) + if (cell->type.in("$_DFF_P_", "$_DFF_N_", "$dff")) { + // not using sigmap -- we want the net directly at the dff output + for (auto bit : cell->getPort("\\Q")) + reg_bits.insert(bit); + } + } + + for (auto wire : module->wires()) { + bool is_register = false; + if (regsmode) + for (auto bit : SigSpec(wire)) + if (reg_bits.count(bit)) + is_register = true; + 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)); + if (wire->port_output) + 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") || (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", log_id(module), log_id(wire), log_id(module), GetSize(sig), get_bv(sig).c_str())); @@ -405,6 +512,118 @@ struct Smt2Worker log_id(module), log_id(wire), log_id(module), get_bool(sig[i]).c_str())); } } + } + + if (verbose) log("=> export logic associated with the initial state\n"); + + vector<string> init_list; + for (auto wire : module->wires()) + if (wire->attributes.count("\\init")) { + RTLIL::SigSpec sig = sigmap(wire); + Const val = wire->attributes.at("\\init"); + val.bits.resize(GetSize(sig)); + if (bvmode && GetSize(sig) > 1) { + init_list.push_back(stringf("(= %s #b%s) ; %s", get_bv(sig).c_str(), val.as_string().c_str(), log_id(wire))); + } else { + for (int i = 0; i < GetSize(sig); i++) + init_list.push_back(stringf("(= %s %s) ; %s", get_bool(sig[i]).c_str(), val.bits[i] == State::S1 ? "true" : "false", log_id(wire))); + } + } + + if (verbose) log("=> export logic driving asserts\n"); + + vector<int> assert_list, assume_list; + for (auto cell : module->cells()) + if (cell->type.in("$assert", "$assume")) { + string name_a = get_bool(cell->getPort("\\A")); + string name_en = get_bool(cell->getPort("\\EN")); + decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool (or %s (not %s))) ; %s\n", + log_id(module), idcounter, log_id(module), name_a.c_str(), name_en.c_str(), log_id(cell))); + if (cell->type == "$assert") + assert_list.push_back(idcounter++); + else + assume_list.push_back(idcounter++); + } + + for (int iter = 1; !registers.empty(); iter++) + { + pool<Cell*> this_regs; + this_regs.swap(registers); + + if (verbose) log("=> export logic driving registers [iteration %d]\n", iter); + + for (auto cell : this_regs) + { + if (cell->type == "$_DFF_P_" || cell->type == "$_DFF_N_") + { + std::string expr_d = get_bool(cell->getPort("\\D")); + std::string expr_q = get_bool(cell->getPort("\\Q"), "next_state"); + trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), log_id(cell), log_signal(cell->getPort("\\Q")))); + } + + if (cell->type == "$dff") + { + std::string expr_d = get_bv(cell->getPort("\\D")); + std::string expr_q = get_bv(cell->getPort("\\Q"), "next_state"); + trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), log_id(cell), log_signal(cell->getPort("\\Q")))); + } + + if (cell->type == "$mem") + { + int arrayid = memarrays.at(cell); + + int abits = cell->getParam("\\ABITS").as_int(); + int width = cell->getParam("\\WIDTH").as_int(); + int wr_ports = cell->getParam("\\WR_PORTS").as_int(); + + for (int i = 0; i < wr_ports; i++) + { + std::string addr = get_bv(cell->getPort("\\WR_ADDR").extract(abits*i, abits)); + std::string data = get_bv(cell->getPort("\\WR_DATA").extract(width*i, width)); + std::string mask = get_bv(cell->getPort("\\WR_EN").extract(width*i, width)); + + data = stringf("(bvor (bvand %s %s) (bvand (select (|%s#%d#%d| state) %s) (bvnot %s)))", + data.c_str(), mask.c_str(), log_id(module), arrayid, i, addr.c_str(), mask.c_str()); + + decls.push_back(stringf("(define-fun |%s#%d#%d| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) " + "(store (|%s#%d#%d| state) %s %s)) ; %s\n", + log_id(module), arrayid, i+1, log_id(module), abits, width, + log_id(module), arrayid, i, addr.c_str(), data.c_str(), log_id(cell))); + } + + std::string expr_d = stringf("(|%s#%d#%d| state)", log_id(module), arrayid, wr_ports); + std::string expr_q = stringf("(|%s#%d#0| next_state)", log_id(module), arrayid); + trans.push_back(stringf(" (= %s %s) ; %s\n", expr_d.c_str(), expr_q.c_str(), log_id(cell))); + } + } + } + + string assert_expr = assert_list.empty() ? "true" : "(and"; + if (!assert_list.empty()) { + for (int i : assert_list) + assert_expr += stringf(" (|%s#%d| state)", log_id(module), i); + assert_expr += ")"; + } + decls.push_back(stringf("(define-fun |%s_a| ((state |%s_s|)) Bool %s)\n", + log_id(module), log_id(module), assert_expr.c_str())); + + string assume_expr = assume_list.empty() ? "true" : "(and"; + if (!assume_list.empty()) { + for (int i : assume_list) + assume_expr += stringf(" (|%s#%d| state)", log_id(module), i); + assume_expr += ")"; + } + decls.push_back(stringf("(define-fun |%s_u| ((state |%s_s|)) Bool %s)\n", + log_id(module), log_id(module), assume_expr.c_str())); + + string init_expr = init_list.empty() ? "true" : "(and"; + if (!init_list.empty()) { + for (auto &str : init_list) + init_expr += stringf("\n\t%s", str.c_str()); + init_expr += "\n)"; + } + decls.push_back(stringf("(define-fun |%s_i| ((state |%s_s|)) Bool %s)\n", + log_id(module), log_id(module), init_expr.c_str())); } void write(std::ostream &f) @@ -412,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"; @@ -436,8 +656,8 @@ struct Smt2Backend : public Backend { log(" write_smt2 [options] [filename]\n"); log("\n"); log("Write a SMT-LIBv2 [1] description of the current design. For a module with name\n"); - log("'<mod>' this will declare the sort '<mod>_s' (state of the module) and the the\n"); - log("function '<mod>_t' (state transition function).\n"); + log("'<mod>' this will declare the sort '<mod>_s' (state of the module) and the\n"); + log("functions operating on that state.\n"); log("\n"); log("The '<mod>_s' sort represents a module state. Additional '<mod>_n' functions\n"); log("are provided that can be used to access the values of the signals in the module.\n"); @@ -449,11 +669,37 @@ struct Smt2Backend : public Backend { log("The '<mod>_t' function evaluates to 'true' when the given pair of states\n"); log("describes a valid state transition.\n"); log("\n"); + log("The '<mod>_a' function evaluates to 'true' when the given state satisfies\n"); + log("the asserts in the module.\n"); + log("\n"); + log("The '<mod>_u' function evaluates to 'true' when the given state satisfies\n"); + log("the assumptions in the module.\n"); + log("\n"); + log("The '<mod>_i' function evaluates to 'true' when the given state conforms\n"); + log("to the initial state.\n"); + log("\n"); + log(" -verbose\n"); + log(" this will print the recursive walk used to export the modules.\n"); + log("\n"); log(" -bv\n"); log(" enable support for BitVec (FixedSizeBitVectors theory). with this\n"); log(" option set multi-bit wires are represented using the BitVec sort and\n"); log(" support for coarse grain cells (incl. arithmetic) is enabled.\n"); log("\n"); + log(" -mem\n"); + log(" enable support for memories (via ArraysEx theory). this option\n"); + log(" also implies -bv. only $mem cells without merged registers in\n"); + log(" read ports are supported. call \"memory\" with -nordff to make sure\n"); + log(" that no registers are merged into $mem read ports. '<mod>_m' functions\n"); + log(" will be generated for accessing the arrays that are used to represent\n"); + log(" memories.\n"); + log("\n"); + 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"); @@ -500,7 +746,7 @@ struct Smt2Backend : public Backend { log("The following yosys script will create a 'test.smt2' file for our proof:\n"); log("\n"); log(" read_verilog test.v\n"); - log(" hierarchy; proc; techmap; opt -fast\n"); + log(" hierarchy -check; proc; opt; check -assert\n"); log(" write_smt2 -bv -tpl test.tpl test.smt2\n"); log("\n"); log("Running 'cvc4 test.smt2' will print 'unsat' because y can never transition\n"); @@ -510,9 +756,9 @@ 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; + bool bvmode = false, memmode = false, regsmode = false, wiresmode = false, verbose = false; - log_header("Executing SMT2 backend.\n"); + log_header(design, "Executing SMT2 backend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -527,6 +773,23 @@ struct Smt2Backend : public Backend { bvmode = true; continue; } + if (args[argidx] == "-mem") { + bvmode = true; + memmode = true; + continue; + } + if (args[argidx] == "-regs") { + regsmode = true; + continue; + } + if (args[argidx] == "-wires") { + wiresmode = true; + continue; + } + if (args[argidx] == "-verbose") { + verbose = true; + continue; + } break; } extra_args(f, filename, args, argidx); @@ -552,7 +815,7 @@ struct Smt2Backend : public Backend { log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module)); - Smt2Worker worker(module, bvmode); + 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 000000000..f2911b3e7 --- /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 000000000..6e8bded77 --- /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/Makefile.inc b/backends/smv/Makefile.inc new file mode 100644 index 000000000..66c192d80 --- /dev/null +++ b/backends/smv/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += backends/smv/smv.o + diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc new file mode 100644 index 000000000..162ce4906 --- /dev/null +++ b/backends/smv/smv.cc @@ -0,0 +1,784 @@ +/* + * 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/rtlil.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/celltypes.h" +#include "kernel/log.h" +#include <string> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct SmvWorker +{ + CellTypes ct; + SigMap sigmap; + RTLIL::Module *module; + std::ostream &f; + bool verbose; + + int idcounter; + dict<IdString, shared_str> idcache; + pool<shared_str> used_names; + vector<shared_str> strbuf; + + pool<Wire*> partial_assignment_wires; + dict<SigBit, std::pair<const char*, int>> partial_assignment_bits; + vector<string> assignments, invarspecs; + + const char *cid() + { + while (true) { + shared_str s(stringf("_%d", idcounter++)); + if (!used_names.count(s)) { + used_names.insert(s); + return s.c_str(); + } + } + } + + const char *cid(IdString id, bool precache = false) + { + if (!idcache.count(id)) + { + string name = stringf("_%s", id.c_str()); + + if (name.substr(0, 2) == "_\\") + name = "_" + name.substr(2); + + for (auto &c : name) { + if (c == '|' || c == '$' || c == '_') continue; + if (c >= 'a' && c <='z') continue; + if (c >= 'A' && c <='Z') continue; + if (c >= '0' && c <='9') continue; + if (precache) return nullptr; + c = '#'; + } + + if (name == "_main") + name = "main"; + + while (used_names.count(name)) + name += "_"; + + shared_str s(name); + used_names.insert(s); + idcache[id] = s; + } + + return idcache.at(id).c_str(); + } + + SmvWorker(RTLIL::Module *module, bool verbose, std::ostream &f) : + ct(module->design), sigmap(module), module(module), f(f), verbose(verbose), idcounter(0) + { + for (auto mod : module->design->modules()) + cid(mod->name, true); + + for (auto wire : module->wires()) + cid(wire->name, true); + + for (auto cell : module->cells()) { + cid(cell->name, true); + cid(cell->type, true); + for (auto &conn : cell->connections()) + cid(conn.first, true); + } + } + + const char *rvalue(SigSpec sig, int width = -1, bool is_signed = false) + { + string s; + int count_chunks = 0; + sigmap.apply(sig); + + for (int i = 0; i < GetSize(sig); i++) + if (partial_assignment_bits.count(sig[i])) + { + int width = 1; + const auto &bit_a = partial_assignment_bits.at(sig[i]); + + while (i+width < GetSize(sig)) + { + if (!partial_assignment_bits.count(sig[i+width])) + break; + + const auto &bit_b = partial_assignment_bits.at(sig[i+width]); + if (strcmp(bit_a.first, bit_b.first)) + break; + if (bit_a.second+width != bit_b.second) + break; + + width++; + } + + if (i+width < GetSize(sig)) + s = stringf("%s :: ", rvalue(sig.extract(i+width, GetSize(sig)-(width+i)))); + + s += stringf("%s[%d:%d]", bit_a.first, bit_a.second+width-1, bit_a.second); + + if (i > 0) + s += stringf(" :: %s", rvalue(sig.extract(0, i))); + + count_chunks = 3; + goto continue_with_resize; + } + + for (auto &c : sig.chunks()) { + count_chunks++; + if (!s.empty()) + s = " :: " + s; + if (c.wire) { + if (c.offset != 0 || c.width != c.wire->width) + s = stringf("%s[%d:%d]", cid(c.wire->name), c.offset+c.width-1, c.offset) + s; + else + s = cid(c.wire->name) + s; + } else { + string v = stringf("0ub%d_", c.width); + for (int i = c.width-1; i >= 0; i--) + v += c.data.at(i) == State::S1 ? '1' : '0'; + s = v + s; + } + } + + continue_with_resize:; + if (width >= 0) { + if (is_signed) { + if (GetSize(sig) > width) + s = stringf("signed(resize(%s, %d))", s.c_str(), width); + else + s = stringf("resize(signed(%s), %d)", s.c_str(), width); + } else + s = stringf("resize(%s, %d)", s.c_str(), width); + } else if (is_signed) + s = stringf("signed(%s)", s.c_str()); + else if (count_chunks > 1) + s = stringf("(%s)", s.c_str()); + + strbuf.push_back(s); + return strbuf.back().c_str(); + } + + const char *rvalue_u(SigSpec sig, int width = -1) + { + return rvalue(sig, width, false); + } + + const char *rvalue_s(SigSpec sig, int width = -1, bool is_signed = true) + { + return rvalue(sig, width, is_signed); + } + + const char *lvalue(SigSpec sig) + { + sigmap.apply(sig); + + if (sig.is_wire()) + return rvalue(sig); + + const char *temp_id = cid(); + f << stringf(" %s : unsigned word[%d]; -- %s\n", temp_id, GetSize(sig), log_signal(sig)); + + int offset = 0; + for (auto bit : sig) { + log_assert(bit.wire != nullptr); + partial_assignment_wires.insert(bit.wire); + partial_assignment_bits[bit] = std::pair<const char*, int>(temp_id, offset++); + } + + return temp_id; + } + + void run() + { + f << stringf("MODULE %s\n", cid(module->name)); + f << stringf(" VAR\n"); + + for (auto wire : module->wires()) + { + if (SigSpec(wire) != sigmap(wire)) + partial_assignment_wires.insert(wire); + + f << stringf(" %s : unsigned word[%d]; -- %s\n", cid(wire->name), wire->width, log_id(wire)); + + if (wire->attributes.count("\\init")) + assignments.push_back(stringf("init(%s) := %s;", lvalue(wire), rvalue(wire->attributes.at("\\init")))); + } + + for (auto cell : module->cells()) + { + // FIXME: $slice, $concat, $mem + + if (cell->type.in("$assert")) + { + SigSpec sig_a = cell->getPort("\\A"); + SigSpec sig_en = cell->getPort("\\EN"); + + invarspecs.push_back(stringf("!bool(%s) | bool(%s);", rvalue(sig_en), rvalue(sig_a))); + + continue; + } + + if (cell->type.in("$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx")) + { + SigSpec sig_a = cell->getPort("\\A"); + SigSpec sig_b = cell->getPort("\\B"); + + int width_y = GetSize(cell->getPort("\\Y")); + int shift_b_width = GetSize(sig_b); + 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 = min(shift_b_width, j); + break; + } + + bool signed_a = cell->getParam("\\A_SIGNED").as_bool(); + bool signed_b = cell->getParam("\\B_SIGNED").as_bool(); + string op = cell->type.in("$shl", "$sshl") ? "<<" : ">>"; + string expr, expr_a; + + if (cell->type == "$sshr" && signed_a) + { + expr_a = rvalue_s(sig_a, width); + expr = stringf("resize(unsigned(%s %s %s), %d)", expr_a.c_str(), op.c_str(), rvalue(sig_b.extract(0, shift_b_width)), width_y); + if (shift_b_width < GetSize(sig_b)) + expr = stringf("%s != 0ud%d_0 ? (bool(%s) ? !0ud%d_0 : 0ud%d_0) : %s", + rvalue(sig_b.extract(shift_b_width, GetSize(sig_b) - shift_b_width)), GetSize(sig_b) - shift_b_width, + rvalue(sig_a[GetSize(sig_a)-1]), width_y, width_y, expr.c_str()); + } + else if (cell->type.in("$shift", "$shiftx") && signed_b) + { + expr_a = rvalue_u(sig_a, width); + + const char *b_shr = rvalue_u(sig_b); + const char *b_shl = cid(); + + f << stringf(" %s : unsigned word[%d]; -- neg(%s)\n", b_shl, GetSize(sig_b), log_signal(sig_b)); + assignments.push_back(stringf("%s := unsigned(-%s);", b_shl, rvalue_s(sig_b))); + + string expr_shl = stringf("resize(%s << %s[%d:0], %d)", expr_a.c_str(), b_shl, shift_b_width-1, width_y); + string expr_shr = stringf("resize(%s >> %s[%d:0], %d)", expr_a.c_str(), b_shr, shift_b_width-1, width_y); + + if (shift_b_width < GetSize(sig_b)) { + expr_shl = stringf("%s[%d:%d] != 0ud%d_0 ? 0ud%d_0 : %s", b_shl, GetSize(sig_b)-1, shift_b_width, + GetSize(sig_b)-shift_b_width, width_y, expr_shl.c_str()); + expr_shr = stringf("%s[%d:%d] != 0ud%d_0 ? 0ud%d_0 : %s", b_shr, GetSize(sig_b)-1, shift_b_width, + GetSize(sig_b)-shift_b_width, width_y, expr_shr.c_str()); + } + + expr = stringf("bool(%s) ? %s : %s", rvalue(sig_b[GetSize(sig_b)-1]), expr_shl.c_str(), expr_shr.c_str()); + } + else + { + if (cell->type.in("$shift", "$shiftx") || !signed_a) + expr_a = rvalue_u(sig_a, width); + else + expr_a = stringf("resize(unsigned(%s), %d)", rvalue_s(sig_a, width_ay), width); + + expr = stringf("resize(%s %s %s[%d:0], %d)", expr_a.c_str(), op.c_str(), rvalue_u(sig_b), shift_b_width-1, width_y); + if (shift_b_width < GetSize(sig_b)) + expr = stringf("%s[%d:%d] != 0ud%d_0 ? 0ud%d_0 : %s", rvalue_u(sig_b), GetSize(sig_b)-1, shift_b_width, + GetSize(sig_b)-shift_b_width, width_y, expr.c_str()); + } + + assignments.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str())); + + continue; + } + + if (cell->type.in("$not", "$pos", "$neg")) + { + int width = GetSize(cell->getPort("\\Y")); + string expr_a, op; + + if (cell->type == "$not") op = "!"; + if (cell->type == "$pos") op = ""; + if (cell->type == "$neg") op = "-"; + + if (cell->getParam("\\A_SIGNED").as_bool()) + { + assignments.push_back(stringf("%s := unsigned(%s%s);", lvalue(cell->getPort("\\Y")), + op.c_str(), rvalue_s(cell->getPort("\\A"), width))); + } + else + { + assignments.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), + op.c_str(), rvalue_u(cell->getPort("\\A"), width))); + } + + continue; + } + + if (cell->type.in("$add", "$sub", "$mul", "$and", "$or", "$xor", "$xnor")) + { + int width = GetSize(cell->getPort("\\Y")); + string expr_a, expr_b, op; + + if (cell->type == "$add") op = "+"; + if (cell->type == "$sub") op = "-"; + if (cell->type == "$mul") op = "*"; + if (cell->type == "$and") op = "&"; + if (cell->type == "$or") op = "|"; + if (cell->type == "$xor") op = "xor"; + if (cell->type == "$xnor") op = "xnor"; + + if (cell->getParam("\\A_SIGNED").as_bool()) + { + assignments.push_back(stringf("%s := unsigned(%s %s %s);", lvalue(cell->getPort("\\Y")), + rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width))); + } + else + { + assignments.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")), + rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width))); + } + + continue; + } + + if (cell->type.in("$div", "$mod")) + { + int width_y = GetSize(cell->getPort("\\Y")); + 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 = "/"; + if (cell->type == "$mod") op = "mod"; + + if (cell->getParam("\\A_SIGNED").as_bool()) + { + assignments.push_back(stringf("%s := resize(unsigned(%s %s %s), %d);", lvalue(cell->getPort("\\Y")), + rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width), width_y)); + } + else + { + assignments.push_back(stringf("%s := resize(%s %s %s, %d);", lvalue(cell->getPort("\\Y")), + rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width), width_y)); + } + + continue; + } + + if (cell->type.in("$eq", "$ne", "$eqx", "$nex", "$lt", "$le", "$ge", "$gt")) + { + int width = max(GetSize(cell->getPort("\\A")), GetSize(cell->getPort("\\B"))); + string expr_a, expr_b, op; + + if (cell->type == "$eq") op = "="; + if (cell->type == "$ne") op = "!="; + if (cell->type == "$eqx") op = "="; + if (cell->type == "$nex") op = "!="; + if (cell->type == "$lt") op = "<"; + if (cell->type == "$le") op = "<="; + if (cell->type == "$ge") op = ">="; + if (cell->type == "$gt") op = ">"; + + if (cell->getParam("\\A_SIGNED").as_bool()) + { + expr_a = stringf("resize(signed(%s), %d)", rvalue(cell->getPort("\\A")), width); + expr_b = stringf("resize(signed(%s), %d)", rvalue(cell->getPort("\\B")), width); + } + else + { + expr_a = stringf("resize(%s, %d)", rvalue(cell->getPort("\\A")), width); + expr_b = stringf("resize(%s, %d)", rvalue(cell->getPort("\\B")), width); + } + + assignments.push_back(stringf("%s := resize(word1(%s %s %s), %d);", lvalue(cell->getPort("\\Y")), + expr_a.c_str(), op.c_str(), expr_b.c_str(), GetSize(cell->getPort("\\Y")))); + + continue; + } + + if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool")) + { + int width_a = GetSize(cell->getPort("\\A")); + int width_y = GetSize(cell->getPort("\\Y")); + const char *expr_a = rvalue(cell->getPort("\\A")); + const char *expr_y = lvalue(cell->getPort("\\Y")); + string expr; + + if (cell->type == "$reduce_and") expr = stringf("%s = !0ub%d_0", expr_a, width_a); + if (cell->type == "$reduce_or") expr = stringf("%s != 0ub%d_0", expr_a, width_a); + if (cell->type == "$reduce_bool") expr = stringf("%s != 0ub%d_0", expr_a, width_a); + + assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y)); + continue; + } + + if (cell->type.in("$reduce_xor", "$reduce_xnor")) + { + int width_y = GetSize(cell->getPort("\\Y")); + const char *expr_y = lvalue(cell->getPort("\\Y")); + string expr; + + for (auto bit : cell->getPort("\\A")) { + if (!expr.empty()) + expr += " xor "; + expr += rvalue(bit); + } + + if (cell->type == "$reduce_xnor") + expr = "!(" + expr + ")"; + + assignments.push_back(stringf("%s := resize(%s, %d);", expr_y, expr.c_str(), width_y)); + continue; + } + + if (cell->type.in("$logic_and", "$logic_or")) + { + int width_a = GetSize(cell->getPort("\\A")); + int width_b = GetSize(cell->getPort("\\B")); + int width_y = GetSize(cell->getPort("\\Y")); + + string expr_a = stringf("(%s != 0ub%d_0)", rvalue(cell->getPort("\\A")), width_a); + string expr_b = stringf("(%s != 0ub%d_0)", rvalue(cell->getPort("\\B")), width_b); + const char *expr_y = lvalue(cell->getPort("\\Y")); + + string expr; + if (cell->type == "$logic_and") expr = expr_a + " & " + expr_b; + if (cell->type == "$logic_or") expr = expr_a + " | " + expr_b; + + assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y)); + continue; + } + + if (cell->type.in("$logic_not")) + { + int width_a = GetSize(cell->getPort("\\A")); + int width_y = GetSize(cell->getPort("\\Y")); + + string expr_a = stringf("(%s = 0ub%d_0)", rvalue(cell->getPort("\\A")), width_a); + const char *expr_y = lvalue(cell->getPort("\\Y")); + + assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a.c_str(), width_y)); + continue; + } + + if (cell->type.in("$mux", "$pmux")) + { + int width = GetSize(cell->getPort("\\Y")); + SigSpec sig_a = cell->getPort("\\A"); + SigSpec sig_b = cell->getPort("\\B"); + SigSpec sig_s = cell->getPort("\\S"); + + string expr; + for (int i = 0; i < GetSize(sig_s); i++) + expr += stringf("bool(%s) ? %s : ", rvalue(sig_s[i]), rvalue(sig_b.extract(i*width, width))); + expr += rvalue(sig_a); + + assignments.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str())); + continue; + } + + if (cell->type == "$dff") + { + assignments.push_back(stringf("next(%s) := %s;", lvalue(cell->getPort("\\Q")), rvalue(cell->getPort("\\D")))); + continue; + } + + if (cell->type.in("$_BUF_", "$_NOT_")) + { + string op = cell->type == "$_NOT_" ? "!" : ""; + assignments.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue(cell->getPort("\\A")))); + continue; + } + + if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_")) + { + string op; + + if (cell->type.in("$_AND_", "$_NAND_")) op = "&"; + if (cell->type.in("$_OR_", "$_NOR_")) op = "|"; + if (cell->type.in("$_XOR_")) op = "xor"; + if (cell->type.in("$_XNOR_")) op = "xnor"; + + if (cell->type.in("$_NAND_", "$_NOR_")) + assignments.push_back(stringf("%s := !(%s %s %s);", lvalue(cell->getPort("\\Y")), + rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B")))); + else + assignments.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")), + rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B")))); + continue; + } + + if (cell->type == "$_MUX_") + { + assignments.push_back(stringf("%s := bool(%s) ? %s : %s;", lvalue(cell->getPort("\\Y")), + rvalue(cell->getPort("\\S")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\A")))); + continue; + } + + if (cell->type == "$_AOI3_") + { + assignments.push_back(stringf("%s := !((%s & %s) | %s);", lvalue(cell->getPort("\\Y")), + rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")))); + continue; + } + + if (cell->type == "$_OAI3_") + { + assignments.push_back(stringf("%s := !((%s | %s) & %s);", lvalue(cell->getPort("\\Y")), + rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")))); + continue; + } + + if (cell->type == "$_AOI4_") + { + assignments.push_back(stringf("%s := !((%s & %s) | (%s & %s));", lvalue(cell->getPort("\\Y")), + rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D")))); + continue; + } + + if (cell->type == "$_OAI4_") + { + assignments.push_back(stringf("%s := !((%s | %s) & (%s | %s));", lvalue(cell->getPort("\\Y")), + rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D")))); + continue; + } + + if (cell->type[0] == '$') + log_error("Found currently unsupported cell type %s (%s.%s).\n", log_id(cell->type), log_id(module), log_id(cell)); + + f << stringf(" %s : %s;\n", cid(cell->name), cid(cell->type)); + + for (auto &conn : cell->connections()) + if (cell->output(conn.first)) + assignments.push_back(stringf("%s := %s.%s;", lvalue(conn.second), cid(cell->name), cid(conn.first))); + else + assignments.push_back(stringf("%s.%s := %s;", cid(cell->name), cid(conn.first), rvalue(conn.second))); + } + + for (Wire *wire : partial_assignment_wires) + { + string expr; + + for (int i = 0; i < wire->width; i++) + { + if (!expr.empty()) + expr = " :: " + expr; + + if (partial_assignment_bits.count(sigmap(SigBit(wire, i)))) + { + int width = 1; + const auto &bit_a = partial_assignment_bits.at(sigmap(SigBit(wire, i))); + + while (i+1 < wire->width) + { + SigBit next_bit = sigmap(SigBit(wire, i+1)); + + if (!partial_assignment_bits.count(next_bit)) + break; + + const auto &bit_b = partial_assignment_bits.at(next_bit); + if (strcmp(bit_a.first, bit_b.first)) + break; + if (bit_a.second+width != bit_b.second) + break; + + width++, i++; + } + + expr = stringf("%s[%d:%d]", bit_a.first, bit_a.second+width-1, bit_a.second) + expr; + } + else if (sigmap(SigBit(wire, i)).wire == nullptr) + { + string bits; + SigSpec sig = sigmap(SigSpec(wire, i)); + + while (i+1 < wire->width) { + SigBit next_bit = sigmap(SigBit(wire, i+1)); + if (next_bit.wire != nullptr) + break; + sig.append(next_bit); + i++; + } + + for (int k = GetSize(sig)-1; k >= 0; k--) + bits += sig[k] == State::S1 ? '1' : '0'; + + expr = stringf("0ub%d_%s", GetSize(bits), bits.c_str()) + expr; + } + else if (sigmap(SigBit(wire, i)) == SigBit(wire, i)) + { + int length = 1; + + while (i+1 < wire->width) { + if (partial_assignment_bits.count(sigmap(SigBit(wire, i+1)))) + break; + if (sigmap(SigBit(wire, i+1)) != SigBit(wire, i+1)) + break; + i++, length++; + } + + expr = stringf("0ub%d_0", length) + expr; + } + else + { + string bits; + SigSpec sig = sigmap(SigSpec(wire, i)); + + while (i+1 < wire->width) { + SigBit next_bit = sigmap(SigBit(wire, i+1)); + if (next_bit.wire == nullptr || partial_assignment_bits.count(next_bit)) + break; + sig.append(next_bit); + i++; + } + + expr = rvalue(sig) + expr; + } + } + + assignments.push_back(stringf("%s := %s;", cid(wire->name), expr.c_str())); + } + + if (!assignments.empty()) { + f << stringf(" ASSIGN\n"); + for (const string &line : assignments) + f << stringf(" %s\n", line.c_str()); + } + + if (!invarspecs.empty()) { + for (const string &line : invarspecs) + f << stringf(" INVARSPEC %s\n", line.c_str()); + } + } +}; + +struct SmvBackend : public Backend { + SmvBackend() : Backend("smv", "write design to SMV file") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_smv [options] [filename]\n"); + log("\n"); + log("Write an SMV description of the current design.\n"); + log("\n"); + log(" -verbose\n"); + log(" this will print the recursive walk used to export the modules.\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"); + log("\n"); + log("THIS COMMAND IS UNDER CONSTRUCTION\n"); + log("\n"); + } + virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + { + std::ifstream template_f; + bool verbose = false; + + log_header(design, "Executing SMV backend.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-tpl" && argidx+1 < args.size()) { + template_f.open(args[++argidx]); + if (template_f.fail()) + log_error("Can't open template file `%s'.\n", args[argidx].c_str()); + continue; + } + if (args[argidx] == "-verbose") { + verbose = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + pool<Module*> modules; + + for (auto module : design->modules()) + if (!module->get_bool_attribute("\\blackbox") && !module->has_memories_warn() && !module->has_processes_warn()) + modules.insert(module); + + if (template_f.is_open()) + { + std::string line; + while (std::getline(template_f, line)) + { + int indent = 0; + while (indent < GetSize(line) && (line[indent] == ' ' || line[indent] == '\t')) + indent++; + + if (line[indent] == '%') + { + vector<string> stmt = split_tokens(line); + + if (GetSize(stmt) == 1 && stmt[0] == "%%") + break; + + if (GetSize(stmt) == 2 && stmt[0] == "%module") + { + Module *module = design->module(RTLIL::escape_id(stmt[1])); + modules.erase(module); + + if (module == nullptr) + log_error("Module '%s' not found.\n", stmt[1].c_str()); + + *f << stringf("-- SMV description generated by %s\n", yosys_version_str); + + log("Creating SMV representation of module %s.\n", log_id(module)); + SmvWorker worker(module, verbose, *f); + worker.run(); + + *f << stringf("-- end of yosys output\n"); + continue; + } + + log_error("Unknown template statement: '%s'", line.c_str() + indent); + } + + *f << line << std::endl; + } + } + + if (!modules.empty()) + { + *f << stringf("-- SMV description generated by %s\n", yosys_version_str); + + for (auto module : modules) { + log("Creating SMV representation of module %s.\n", log_id(module)); + SmvWorker worker(module, verbose, *f); + worker.run(); + } + + *f << stringf("-- end of yosys output\n"); + } + + if (template_f.is_open()) { + std::string line; + while (std::getline(template_f, line)) + *f << line << std::endl; + } + } +} SmvBackend; + +PRIVATE_NAMESPACE_END diff --git a/backends/smv/test_cells.sh b/backends/smv/test_cells.sh new file mode 100644 index 000000000..63de465c0 --- /dev/null +++ b/backends/smv/test_cells.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -ex + +rm -rf test_cells.tmp +mkdir -p test_cells.tmp +cd test_cells.tmp + +# don't test $mul to reduce runtime +# don't test $div and $mod to reduce runtime and avoid "div by zero" message +../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$macc /$mul /$div /$mod' + +cat > template.txt << "EOT" +%module main + INVARSPEC ! bool(_trigger); +EOT + +for fn in test_*.il; do + ../../../yosys -p " + read_ilang $fn + rename gold gate + synth + + read_ilang $fn + miter -equiv -flatten gold gate main + hierarchy -top main + write_smv -tpl template.txt ${fn#.il}.smv + " + nuXmv -dynamic ${fn#.il}.smv > ${fn#.il}.out +done + +grep '^-- invariant .* is false' *.out || echo 'All OK.' + diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc index 2c614178b..4101cbf98 100644 --- a/backends/spice/spice.cc +++ b/backends/spice/spice.cc @@ -2,11 +2,11 @@ * 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 @@ -27,13 +27,33 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -static void print_spice_net(std::ostream &f, RTLIL::SigBit s, std::string &neg, std::string &pos, std::string &ncpf, int &nc_counter) +static string spice_id2str(IdString id) +{ + static const char *escape_chars = "$\\[]()<>="; + string s = RTLIL::unescape_id(id); + + for (auto &ch : s) + if (strchr(escape_chars, ch) != nullptr) ch = '_'; + + return s; +} + +static string spice_id2str(IdString id, bool use_inames, idict<IdString, 1> &inums) +{ + if (!use_inames && *id.c_str() == '$') + return stringf("%d", inums(id)); + return spice_id2str(id); +} + +static void print_spice_net(std::ostream &f, RTLIL::SigBit s, std::string &neg, std::string &pos, std::string &ncpf, int &nc_counter, bool use_inames, idict<IdString, 1> &inums) { if (s.wire) { + if (s.wire->port_id) + use_inames = true; if (s.wire->width > 1) - f << stringf(" %s[%d]", RTLIL::id2cstr(s.wire->name), s.offset); + f << stringf(" %s.%d", spice_id2str(s.wire->name, use_inames, inums).c_str(), s.offset); else - f << stringf(" %s", RTLIL::id2cstr(s.wire->name)); + f << stringf(" %s", spice_id2str(s.wire->name, use_inames, inums).c_str()); } else { if (s == RTLIL::State::S0) f << stringf(" %s", neg.c_str()); @@ -44,9 +64,10 @@ static void print_spice_net(std::ostream &f, RTLIL::SigBit s, std::string &neg, } } -static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, std::string &neg, std::string &pos, std::string &ncpf, bool big_endian) +static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, std::string &neg, std::string &pos, std::string &ncpf, bool big_endian, bool use_inames) { SigMap sigmap(module); + idict<IdString, 1> inums; int cell_counter = 0, conn_counter = 0, nc_counter = 0; for (auto &cell_it : module->cells_) @@ -59,7 +80,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De if (design->modules_.count(cell->type) == 0) { log_warning("no (blackbox) module for cell type `%s' (%s.%s) found! Guessing order of ports.\n", - RTLIL::id2cstr(cell->type), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name)); + log_id(cell->type), log_id(module), log_id(cell)); for (auto &conn : cell->connections()) { RTLIL::SigSpec sig = sigmap(conn.second); port_sigs.push_back(sig); @@ -93,18 +114,18 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De for (auto &sig : port_sigs) { for (int i = 0; i < sig.size(); i++) { RTLIL::SigSpec s = sig.extract(big_endian ? sig.size() - 1 - i : i, 1); - print_spice_net(f, s, neg, pos, ncpf, nc_counter); + print_spice_net(f, s, neg, pos, ncpf, nc_counter, use_inames, inums); } } - f << stringf(" %s\n", RTLIL::id2cstr(cell->type)); + f << stringf(" %s\n", spice_id2str(cell->type).c_str()); } for (auto &conn : module->connections()) for (int i = 0; i < conn.first.size(); i++) { f << stringf("V%d", conn_counter++); - print_spice_net(f, conn.first.extract(i, 1), neg, pos, ncpf, nc_counter); - print_spice_net(f, conn.second.extract(i, 1), neg, pos, ncpf, nc_counter); + print_spice_net(f, conn.first.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums); + print_spice_net(f, conn.second.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums); f << stringf(" DC 0\n"); } } @@ -120,7 +141,7 @@ struct SpiceBackend : public Backend { log("Write the current design to an SPICE netlist file.\n"); log("\n"); log(" -big_endian\n"); - log(" generate multi-bit ports in MSB first order \n"); + log(" generate multi-bit ports in MSB first order\n"); log(" (default is LSB first)\n"); log("\n"); log(" -neg net_name\n"); @@ -132,6 +153,10 @@ struct SpiceBackend : public Backend { log(" -nc_prefix\n"); log(" prefix for not-connected nets (default: _NC)\n"); log("\n"); + log(" -inames\n"); + log(" include names of internal ($-prefixed) nets in outputs\n"); + log(" (default is to use net numbers instead)\n"); + log("\n"); log(" -top top_module\n"); log(" set the specified module as design top module\n"); log("\n"); @@ -140,10 +165,10 @@ struct SpiceBackend : public Backend { { std::string top_module_name; RTLIL::Module *top_module = NULL; - bool big_endian = false; + bool big_endian = false, use_inames = false; std::string neg = "Vss", pos = "Vdd", ncpf = "_NC"; - log_header("Executing SPICE backend.\n"); + log_header(design, "Executing SPICE backend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -152,6 +177,10 @@ struct SpiceBackend : public Backend { big_endian = true; continue; } + if (args[argidx] == "-inames") { + use_inames = true; + continue; + } if (args[argidx] == "-neg" && argidx+1 < args.size()) { neg = args[++argidx]; continue; @@ -187,9 +216,9 @@ struct SpiceBackend : public Backend { continue; if (module->processes.size() != 0) - log_error("Found unmapped processes in module %s: unmapped processes are not supported in SPICE backend!\n", RTLIL::id2cstr(module->name)); + log_error("Found unmapped processes in module %s: unmapped processes are not supported in SPICE backend!\n", log_id(module)); if (module->memories.size() != 0) - log_error("Found munmapped emories in module %s: unmapped memories are not supported in SPICE backend!\n", RTLIL::id2cstr(module->name)); + log_error("Found unmapped memories in module %s: unmapped memories are not supported in SPICE backend!\n", log_id(module)); if (module->name == RTLIL::escape_id(top_module_name)) { top_module = module; @@ -206,24 +235,24 @@ struct SpiceBackend : public Backend { ports.at(wire->port_id-1) = wire; } - *f << stringf(".SUBCKT %s", RTLIL::id2cstr(module->name)); + *f << stringf(".SUBCKT %s", spice_id2str(module->name).c_str()); for (RTLIL::Wire *wire : ports) { log_assert(wire != NULL); if (wire->width > 1) { for (int i = 0; i < wire->width; i++) - *f << stringf(" %s[%d]", RTLIL::id2cstr(wire->name), big_endian ? wire->width - 1 - i : i); + *f << stringf(" %s.%d", spice_id2str(wire->name).c_str(), big_endian ? wire->width - 1 - i : i); } else - *f << stringf(" %s", RTLIL::id2cstr(wire->name)); + *f << stringf(" %s", spice_id2str(wire->name).c_str()); } *f << stringf("\n"); - print_spice_module(*f, module, design, neg, pos, ncpf, big_endian); - *f << stringf(".ENDS %s\n\n", RTLIL::id2cstr(module->name)); + print_spice_module(*f, module, design, neg, pos, ncpf, big_endian, use_inames); + *f << stringf(".ENDS %s\n\n", spice_id2str(module->name).c_str()); } if (!top_module_name.empty()) { if (top_module == NULL) log_error("Can't find top module `%s'!\n", top_module_name.c_str()); - print_spice_module(*f, top_module, design, neg, pos, ncpf, big_endian); + print_spice_module(*f, top_module, design, neg, pos, ncpf, big_endian, use_inames); *f << stringf("\n"); } diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index ab0844d72..c5c6b5a08 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -2,11 +2,11 @@ * 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 @@ -17,18 +17,14 @@ * * --- * - * A simple and straightforward verilog backend. - * - * Note that RTLIL processes can't always be mapped easily to a Verilog - * process. Therefore this frontend should only be used to export a - * Verilog netlist (i.e. after the "proc" pass has converted all processes - * to logic networks and registers). + * A simple and straightforward Verilog backend. * */ #include "kernel/register.h" #include "kernel/celltypes.h" #include "kernel/log.h" +#include "kernel/sigtools.h" #include <string> #include <sstream> #include <set> @@ -93,16 +89,18 @@ void reset_auto_counter(RTLIL::Module *module) log(" renaming `%s' to `_%0*d_'.\n", it->first.c_str(), auto_name_digits, auto_name_offset + it->second); } +std::string next_auto_id() +{ + return stringf("_%0*d_", auto_name_digits, auto_name_offset + auto_name_counter++); +} + std::string id(RTLIL::IdString internal_id, bool may_rename = true) { const char *str = internal_id.c_str(); bool do_escape = false; - if (may_rename && auto_name_map.count(internal_id) != 0) { - char buffer[100]; - snprintf(buffer, 100, "_%0*d_", auto_name_digits, auto_name_offset + auto_name_map[internal_id]); - return std::string(buffer); - } + if (may_rename && auto_name_map.count(internal_id) != 0) + return stringf("_%0*d_", auto_name_digits, auto_name_offset + auto_name_map[internal_id]); if (*str == '\\') str++; @@ -151,7 +149,7 @@ bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name) return true; } -void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool set_signed = false) +void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool set_signed = false, bool escape_comment = false) { if (width < 0) width = data.bits.size() - offset; @@ -162,12 +160,13 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o log_assert(i < (int)data.bits.size()); if (data.bits[i] != RTLIL::S0 && data.bits[i] != RTLIL::S1) goto dump_bits; - if (data.bits[i] == RTLIL::S1 && (i - offset) == 31) - goto dump_bits; if (data.bits[i] == RTLIL::S1) val |= 1 << (i - offset); } - f << stringf("32'%sd %d", set_signed ? "s" : "", val); + if (set_signed && val < 0) + f << stringf("-32'sd %u", -val); + else + f << stringf("32'%sd %u", set_signed ? "s" : "", val); } else { dump_bits: f << stringf("%d'%sb", width, set_signed ? "s" : ""); @@ -199,6 +198,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o f << stringf("\\\""); else if (str[i] == '\\') f << stringf("\\\\"); + else if (str[i] == '/' && escape_comment && i > 0 && str[i-1] == '*') + f << stringf("\\/"); else f << str[i]; } @@ -258,7 +259,7 @@ void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, else if (modattr && (it->second == Const(1, 1) || it->second == Const(1))) f << stringf(" 1 "); else - dump_const(f, it->second); + dump_const(f, it->second, -1, 0, false, false, attr2comment); f << stringf(" %s%c", attr2comment ? "*/" : "*)", term); } } @@ -279,7 +280,7 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) f << stringf("[%d:%d] ", wire->width - 1 + wire->start_offset, wire->start_offset); f << stringf("%s;\n", id(wire->name).c_str()); #else - // do not use Verilog-2k "outut reg" syntax in verilog export + // do not use Verilog-2k "output reg" syntax in Verilog export std::string range = ""; if (wire->width != 1) { if (wire->upto) @@ -293,9 +294,14 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) f << stringf("%s" "output%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); if (wire->port_input && wire->port_output) f << stringf("%s" "inout%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); - if (reg_wires.count(wire->name)) + if (reg_wires.count(wire->name)) { f << stringf("%s" "reg%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); - else if (!wire->port_input && !wire->port_output) + if (wire->attributes.count("\\init")) { + f << stringf("%s" "initial %s = ", indent.c_str(), id(wire->name).c_str()); + dump_const(f, wire->attributes.at("\\init")); + f << stringf(";\n"); + } + } else if (!wire->port_input && !wire->port_output) f << stringf("%s" "wire%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); #endif } @@ -669,6 +675,56 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } + if (cell->type == "$dffsr") + { + SigSpec sig_clk = cell->getPort("\\CLK"); + SigSpec sig_set = cell->getPort("\\SET"); + SigSpec sig_clr = cell->getPort("\\CLR"); + SigSpec sig_d = cell->getPort("\\D"); + SigSpec sig_q = cell->getPort("\\Q"); + + int width = cell->parameters["\\WIDTH"].as_int(); + bool pol_clk = cell->parameters["\\CLK_POLARITY"].as_bool(); + bool pol_set = cell->parameters["\\SET_POLARITY"].as_bool(); + bool pol_clr = cell->parameters["\\CLR_POLARITY"].as_bool(); + + std::string reg_name = cellname(cell); + bool out_is_reg_wire = is_reg_wire(sig_q, reg_name); + + if (!out_is_reg_wire) + f << stringf("%s" "reg [%d:0] %s;\n", indent.c_str(), width-1, reg_name.c_str()); + + for (int i = 0; i < width; i++) { + f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg"); + dump_sigspec(f, sig_clk); + f << stringf(", %sedge ", pol_set ? "pos" : "neg"); + dump_sigspec(f, sig_set); + f << stringf(", %sedge ", pol_clr ? "pos" : "neg"); + dump_sigspec(f, sig_clr); + f << stringf(")\n"); + + f << stringf("%s" " if (%s", indent.c_str(), pol_clr ? "" : "!"); + dump_sigspec(f, sig_clr); + f << stringf(") %s[%d] <= 1'b0;\n", reg_name.c_str(), i); + + f << stringf("%s" " else if (%s", indent.c_str(), pol_set ? "" : "!"); + dump_sigspec(f, sig_set); + f << stringf(") %s[%d] <= 1'b1;\n", reg_name.c_str(), i); + + f << stringf("%s" " else %s[%d] <= ", indent.c_str(), reg_name.c_str(), i); + dump_sigspec(f, sig_d[i]); + f << stringf(";\n"); + } + + if (!out_is_reg_wire) { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, sig_q); + f << stringf(" = %s;\n", reg_name.c_str()); + } + + return true; + } + if (cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffe") { RTLIL::SigSpec sig_clk, sig_arst, sig_en, val_arst; @@ -731,8 +787,229 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } + if (cell->type == "$mem") + { + RTLIL::IdString memid = cell->parameters["\\MEMID"].decode_string(); + std::string mem_id = id(cell->parameters["\\MEMID"].decode_string()); + int abits = cell->parameters["\\ABITS"].as_int(); + int size = cell->parameters["\\SIZE"].as_int(); + int width = cell->parameters["\\WIDTH"].as_int(); + bool use_init = !(RTLIL::SigSpec(cell->parameters["\\INIT"]).is_fully_undef()); + + // for memory block make something like: + // reg [7:0] memid [3:0]; + // initial begin + // memid[0] <= ... + // end + f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size-1, 0); + if (use_init) + { + f << stringf("%s" "initial begin\n", indent.c_str()); + for (int i=0; i<size; i++) + { + f << stringf("%s" " %s[%d] <= ", indent.c_str(), mem_id.c_str(), i); + dump_const(f, cell->parameters["\\INIT"].extract(i*width, width)); + f << stringf(";\n"); + } + f << stringf("%s" "end\n", indent.c_str()); + } + + // create a map : "edge clk" -> expressions within that clock domain + dict<std::string, std::vector<std::string>> clk_to_lof_body; + clk_to_lof_body[""] = std::vector<std::string>(); + std::string clk_domain_str; + // create a list of reg declarations + std::vector<std::string> lof_reg_declarations; + + int nread_ports = cell->parameters["\\RD_PORTS"].as_int(); + RTLIL::SigSpec sig_rd_clk, sig_rd_data, sig_rd_addr; + bool use_rd_clk, rd_clk_posedge, rd_transparent; + // read ports + for (int i=0; i < nread_ports; i++) + { + sig_rd_clk = cell->getPort("\\RD_CLK").extract(i); + sig_rd_data = cell->getPort("\\RD_DATA").extract(i*width, width); + sig_rd_addr = cell->getPort("\\RD_ADDR").extract(i*abits, abits); + use_rd_clk = cell->parameters["\\RD_CLK_ENABLE"].extract(i).as_bool(); + rd_clk_posedge = cell->parameters["\\RD_CLK_POLARITY"].extract(i).as_bool(); + rd_transparent = cell->parameters["\\RD_TRANSPARENT"].extract(i).as_bool(); + { + std::ostringstream os; + dump_sigspec(os, sig_rd_clk); + clk_domain_str = stringf("%sedge %s", rd_clk_posedge ? "pos" : "neg", os.str().c_str()); + if( clk_to_lof_body.count(clk_domain_str) == 0 ) + clk_to_lof_body[clk_domain_str] = std::vector<std::string>(); + } + if (use_rd_clk && !rd_transparent) + { + // for clocked read ports make something like: + // reg [..] temp_id; + // always @(posedge clk) + // temp_id <= array_reg[r_addr]; + // assign r_data = temp_id; + std::string temp_id = next_auto_id(); + lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_data.size() - 1, temp_id.c_str()) ); + { + std::ostringstream os; + dump_sigspec(os, sig_rd_addr); + std::string line = stringf("%s <= %s[%s];\n", temp_id.c_str(), mem_id.c_str(), os.str().c_str()); + clk_to_lof_body[clk_domain_str].push_back(line); + } + { + std::ostringstream os; + dump_sigspec(os, sig_rd_data); + std::string line = stringf("assign %s = %s;\n", os.str().c_str(), temp_id.c_str()); + clk_to_lof_body[""].push_back(line); + } + } else { + if (rd_transparent) { + // for rd-transparent read-ports make something like: + // reg [..] temp_id; + // always @(posedge clk) + // temp_id <= r_addr; + // assign r_data = array_reg[temp_id]; + std::string temp_id = next_auto_id(); + lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_addr.size() - 1, temp_id.c_str()) ); + { + std::ostringstream os; + dump_sigspec(os, sig_rd_addr); + std::string line = stringf("%s <= %s;\n", temp_id.c_str(), os.str().c_str()); + clk_to_lof_body[clk_domain_str].push_back(line); + } + { + std::ostringstream os; + dump_sigspec(os, sig_rd_data); + std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), temp_id.c_str()); + clk_to_lof_body[""].push_back(line); + } + } else { + // for non-clocked read-ports make something like: + // assign r_data = array_reg[r_addr]; + std::ostringstream os, os2; + dump_sigspec(os, sig_rd_data); + dump_sigspec(os2, sig_rd_addr); + std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str()); + clk_to_lof_body[""].push_back(line); + } + } + } + + int nwrite_ports = cell->parameters["\\WR_PORTS"].as_int(); + RTLIL::SigSpec sig_wr_clk, sig_wr_data, sig_wr_addr, sig_wr_en, sig_wr_en_bit; + RTLIL::SigBit last_bit; + bool wr_clk_posedge; + RTLIL::SigSpec lof_wen; + dict<RTLIL::SigSpec, int> wen_to_width; + SigMap sigmap(active_module); + int n, wen_width; + // write ports + for (int i=0; i < nwrite_ports; i++) + { + sig_wr_clk = cell->getPort("\\WR_CLK").extract(i); + sig_wr_data = cell->getPort("\\WR_DATA").extract(i*width, width); + sig_wr_addr = cell->getPort("\\WR_ADDR").extract(i*abits, abits); + sig_wr_en = cell->getPort("\\WR_EN").extract(i*width, width); + sig_wr_en_bit = sig_wr_en.extract(0); + wr_clk_posedge = cell->parameters["\\WR_CLK_POLARITY"].extract(i).as_bool(); + { + std::ostringstream os; + dump_sigspec(os, sig_wr_clk); + clk_domain_str = stringf("%sedge %s", wr_clk_posedge ? "pos" : "neg", os.str().c_str()); + if( clk_to_lof_body.count(clk_domain_str) == 0 ) + clk_to_lof_body[clk_domain_str] = std::vector<std::string>(); + } + // group the wen bits + last_bit = sig_wr_en.extract(0); + lof_wen = RTLIL::SigSpec(last_bit); + wen_to_width.clear(); + wen_to_width[last_bit] = 0; + for (auto ¤t_bit : sig_wr_en.bits()) + { + if (sigmap(current_bit) == sigmap(last_bit)){ + wen_to_width[current_bit] += 1; + } else { + lof_wen.append_bit(current_bit); + wen_to_width[current_bit] = 1; + } + last_bit = current_bit; + } + // make something like: + // always @(posedge clk) + // if (wr_en_bit) memid[w_addr][??] <= w_data[??]; + // ... + n = 0; + for (auto &wen_bit : lof_wen) { + wen_width = wen_to_width[wen_bit]; + if (!(wen_bit == RTLIL::SigBit(false))) + { + std::ostringstream os; + if (!(wen_bit == RTLIL::SigBit(true))) + { + os << stringf("if ("); + dump_sigspec(os, wen_bit); + os << stringf(") "); + } + os << stringf("%s[", mem_id.c_str()); + dump_sigspec(os, sig_wr_addr); + if (wen_width == width) + os << stringf("] <= "); + else + os << stringf("][%d:%d] <= ", n+wen_width-1, n); + dump_sigspec(os, sig_wr_data.extract(n, wen_width)); + os << stringf(";\n"); + clk_to_lof_body[clk_domain_str].push_back(os.str()); + } + n += wen_width; + } + } + // Output Verilog that looks something like this: + // reg [..] _3_; + // always @(posedge CLK2) begin + // _3_ <= memory[D1ADDR]; + // if (A1EN) + // memory[A1ADDR] <= A1DATA; + // if (A2EN) + // memory[A2ADDR] <= A2DATA; + // ... + // end + // always @(negedge CLK1) begin + // if (C1EN) + // memory[C1ADDR] <= C1DATA; + // end + // ... + // assign D1DATA = _3_; + // assign D2DATA <= memory[D2ADDR]; + + // the reg ... definitions + for(auto ® : lof_reg_declarations) + { + f << stringf("%s" "%s", indent.c_str(), reg.c_str()); + } + // the block of expressions by clock domain + for(auto &pair : clk_to_lof_body) + { + std::string clk_domain = pair.first; + std::vector<std::string> lof_lines = pair.second; + if( clk_domain != "") + { + f << stringf("%s" "always @(%s) begin\n", indent.c_str(), clk_domain.c_str()); + for(auto &line : lof_lines) + f << stringf("%s%s" "%s", indent.c_str(), indent.c_str(), line.c_str()); + f << stringf("%s" "end\n", indent.c_str()); + } + else + { + // the non-clocked assignments + for(auto &line : lof_lines) + f << stringf("%s" "%s", indent.c_str(), line.c_str()); + } + } + + return true; + } + // FIXME: $_SR_[PN][PN]_, $_DLATCH_[PN]_, $_DLATCHSR_[PN][PN][PN]_ - // FIXME: $sr, $dffsr, $dlatch, $memrd, $memwr, $mem, $fsm + // FIXME: $sr, $dlatch, $memrd, $memwr, $fsm return false; } @@ -853,11 +1130,15 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw dump_sigspec(f, sw->signal); f << stringf(")\n"); + bool got_default = false; for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { - f << stringf("%s ", indent.c_str()); - if ((*it)->compare.size() == 0) - f << stringf("default"); - else { + if ((*it)->compare.size() == 0) { + if (got_default) + continue; + f << stringf("%s default", indent.c_str()); + got_default = true; + } else { + f << stringf("%s ", indent.c_str()); for (size_t i = 0; i < (*it)->compare.size(); i++) { if (i > 0) f << stringf(", "); @@ -962,6 +1243,12 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) reset_auto_counter(module); active_module = module; + if (!module->processes.empty()) + log_warning("Module %s contains unmapped RTLIL proccesses. RTLIL processes\n" + "can't always be mapped directly to Verilog always blocks. Unintended\n" + "changes in simulation behavior are possible! Use \"proc\" to convert\n" + "processes to logic networks and registers.", log_id(module)); + f << stringf("\n"); for (auto it = module->processes.begin(); it != module->processes.end(); ++it) dump_process(f, indent + " ", it->second, true); @@ -1034,14 +1321,14 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) } struct VerilogBackend : public Backend { - VerilogBackend() : Backend("verilog", "write design to verilog file") { } + VerilogBackend() : Backend("verilog", "write design to Verilog file") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_verilog [options] [filename]\n"); log("\n"); - log("Write the current design to a verilog file.\n"); + log("Write the current design to a Verilog file.\n"); log("\n"); log(" -norename\n"); log(" without this option all internal object names (the ones with a dollar\n"); @@ -1055,7 +1342,7 @@ struct VerilogBackend : public Backend { log(" with this option attributes are included as comments in the output\n"); log("\n"); log(" -noexpr\n"); - log(" without this option all internal cells are converted to verilog\n"); + log(" without this option all internal cells are converted to Verilog\n"); log(" expressions.\n"); log("\n"); log(" -blackboxes\n"); @@ -1067,10 +1354,16 @@ struct VerilogBackend : public Backend { log(" only write selected modules. modules must be selected entirely or\n"); log(" not at all.\n"); log("\n"); + log("Note that RTLIL processes can't always be mapped directly to Verilog\n"); + log("always blocks. This frontend should only be used to export an RTLIL\n"); + log("netlist, i.e. after the \"proc\" pass has been used to convert all\n"); + log("processes to logic networks and registers. A warning is generated when\n"); + log("this command is called on a design with RTLIL processes.\n"); + log("\n"); } virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing Verilog backend.\n"); + log_header(design, "Executing Verilog backend.\n"); norename = false; noattr = false; diff --git a/techlibs/xilinx/example_basys3/README b/examples/basys3/README index 85b6eab10..0ce717294 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 2b01a22a8..2b01a22a8 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 c1fd0e925..c1fd0e925 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 10f059103..10f059103 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 d711af840..d711af840 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 c3b6a610e..c3b6a610e 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 4541826d3..4541826d3 100644 --- a/techlibs/xilinx/example_basys3/run_yosys.ys +++ b/examples/basys3/run_yosys.ys diff --git a/examples/cmos/.gitignore b/examples/cmos/.gitignore new file mode 100644 index 000000000..f58d95018 --- /dev/null +++ b/examples/cmos/.gitignore @@ -0,0 +1,4 @@ +counter_tb +counter_tb.vcd +synth.sp +synth.v diff --git a/examples/cmos/README b/examples/cmos/README new file mode 100644 index 000000000..c459b4b54 --- /dev/null +++ b/examples/cmos/README @@ -0,0 +1,13 @@ + +In this directory contains an example for generating a spice output using two +different spice modes, normal analog transient simulation and event-driven +digital simulation as supported by ngspice xspice sub-module. + +Each test bench can be run separately by either running: + +- testbench.sh, to start analog simulation or +- testbench_digital.sh for mixed-signal digital simulation. + +The later case also includes pure verilog simulation using the iverilog +and gtkwave for comparison. + diff --git a/techlibs/cmos/cmos_cells.lib b/examples/cmos/cmos_cells.lib index bf6b34788..1b0bf8457 100644 --- a/techlibs/cmos/cmos_cells.lib +++ b/examples/cmos/cmos_cells.lib @@ -39,7 +39,7 @@ library(demo) { } cell(DFFSR) { area: 18; - ff(IQ, IQN) { clocked_on: C; + ff("IQ", "IQN") { clocked_on: C; next_state: D; preset: S; clear: R; } @@ -50,5 +50,6 @@ library(demo) { function: "IQ"; } pin(S) { direction: input; } pin(R) { direction: input; } + ; // empty statement } } diff --git a/techlibs/cmos/cmos_cells.sp b/examples/cmos/cmos_cells.sp index 673b20d08..673b20d08 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 da75270cb..27278facb 100644 --- a/techlibs/cmos/cmos_cells.v +++ b/examples/cmos/cmos_cells.v @@ -1,17 +1,26 @@ +module BUF(A, Y); +input A; +output Y; +assign Y = A; +endmodule + module NOT(A, Y); input A; -output Y = ~A; +output Y; +assign Y = ~A; endmodule module NAND(A, B, Y); input A, B; -output Y = ~(A & B); +output Y; +assign Y = ~(A & B); endmodule module NOR(A, B, Y); input A, B; -output Y = ~(A | B); +output Y; +assign Y = ~(A | B); endmodule module DFF(C, D, Q); diff --git a/examples/cmos/cmos_cells_digital.sp b/examples/cmos/cmos_cells_digital.sp new file mode 100644 index 000000000..e1cb82a2f --- /dev/null +++ b/examples/cmos/cmos_cells_digital.sp @@ -0,0 +1,31 @@ + +.SUBCKT BUF A Y +.model buffer1 d_buffer +Abuf A Y buffer1 +.ENDS NOT + +.SUBCKT NOT A Y +.model not1 d_inverter +Anot A Y not1 +.ENDS NOT + +.SUBCKT NAND A B Y +.model nand1 d_nand +Anand [A B] Y nand1 +.ENDS NAND + +.SUBCKT NOR A B Y +.model nor1 d_nor +Anand [A B] Y nor1 +.ENDS NOR + +.SUBCKT DLATCH E D Q +.model latch1 d_latch +Alatch D E null null Q nQ latch1 +.ENDS DLATCH + +.SUBCKT DFF C D Q +.model dff1 d_dff +Adff D C null null Q nQ dff1 +.ENDS DFF + diff --git a/techlibs/cmos/counter.v b/examples/cmos/counter.v index 68b5c05b6..f21658724 100644 --- a/techlibs/cmos/counter.v +++ b/examples/cmos/counter.v @@ -2,7 +2,7 @@ module counter (clk, rst, en, count); input clk, rst, en; output reg [2:0] count; - + always @(posedge clk) if (rst) count <= 3'd0; diff --git a/techlibs/cmos/counter.ys b/examples/cmos/counter.ys index a784f3465..a784f3465 100644 --- a/techlibs/cmos/counter.ys +++ b/examples/cmos/counter.ys diff --git a/examples/cmos/counter_digital.ys b/examples/cmos/counter_digital.ys new file mode 100644 index 000000000..a5e728e02 --- /dev/null +++ b/examples/cmos/counter_digital.ys @@ -0,0 +1,16 @@ + +read_verilog counter.v +read_verilog -lib cmos_cells.v + +proc;; memory;; techmap;; + +dfflibmap -liberty cmos_cells.lib +abc -liberty cmos_cells.lib;; + +# http://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/cadence/lib/tsmc025/signalstorm/osu025_stdcells.lib +# dfflibmap -liberty osu025_stdcells.lib +# abc -liberty osu025_stdcells.lib;; + +write_verilog synth.v +write_spice -neg 0s -pos 1s synth.sp + diff --git a/examples/cmos/counter_tb.gtkw b/examples/cmos/counter_tb.gtkw new file mode 100644 index 000000000..4a2eac400 --- /dev/null +++ b/examples/cmos/counter_tb.gtkw @@ -0,0 +1,5 @@ +[dumpfile] "counter_tb.vcd" +counter_tb.clk +counter_tb.count[2:0] +counter_tb.en +counter_tb.reset diff --git a/examples/cmos/counter_tb.v b/examples/cmos/counter_tb.v new file mode 100644 index 000000000..bcd7d992c --- /dev/null +++ b/examples/cmos/counter_tb.v @@ -0,0 +1,33 @@ +module counter_tb; + + /* Make a reset pulse and specify dump file */ + reg reset = 0; + initial begin + $dumpfile("counter_tb.vcd"); + $dumpvars(0,counter_tb); + + # 0 reset = 1; + # 4 reset = 0; + # 36 reset = 1; + # 4 reset = 0; + # 6 $finish; + end + + /* Make enable with period of 8 and 6,7 low */ + reg en = 1; + always begin + en = 1; + #6; + en = 0; + #2; + end + + /* Make a regular pulsing clock. */ + reg clk = 0; + always #1 clk = !clk; + + /* UUT */ + wire [2:0] count; + counter c1 (clk, reset, en, count); + +endmodule diff --git a/techlibs/cmos/testbench.sh b/examples/cmos/testbench.sh index 061704b64..061704b64 100644 --- a/techlibs/cmos/testbench.sh +++ b/examples/cmos/testbench.sh diff --git a/techlibs/cmos/testbench.sp b/examples/cmos/testbench.sp index 95d2f67cd..e571d2815 100644 --- a/techlibs/cmos/testbench.sp +++ b/examples/cmos/testbench.sp @@ -9,8 +9,8 @@ Vdd Vdd 0 DC 3 .MODEL cmosp PMOS LEVEL=1 VT0=-0.7 KP=50U GAMMA=0.57 LAMBDA=0.05 PHI=0.8 * load design and library -.include synth.sp .include cmos_cells.sp +.include synth.sp * input signals Vclk clk 0 PULSE(0 3 1 0.1 0.1 0.8 2) diff --git a/examples/cmos/testbench_digital.sh b/examples/cmos/testbench_digital.sh new file mode 100644 index 000000000..afaaf4d43 --- /dev/null +++ b/examples/cmos/testbench_digital.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -ex + +# iverlog simulation +echo "Doing Verilog simulation with iverilog" +iverilog -o counter_tb counter.v counter_tb.v +./counter_tb; gtkwave counter_tb.gtkw & + +# yosys synthesis +../../yosys counter_digital.ys + +# requires ngspice with xspice support enabled: +ngspice testbench_digital.sp + diff --git a/examples/cmos/testbench_digital.sp b/examples/cmos/testbench_digital.sp new file mode 100644 index 000000000..c5f9d5987 --- /dev/null +++ b/examples/cmos/testbench_digital.sp @@ -0,0 +1,26 @@ + +* load design and library +.include cmos_cells_digital.sp +.include synth.sp + +* input signals +Vclk clk 0 PULSE(0 3 1 0.1 0.1 0.8 2) +Vrst rst 0 PULSE(0 3 0.5 0.1 0.1 2.9 40) +Ven en 0 PULSE(0 3 0.5 0.1 0.1 5.9 8) + +Xuut dclk drst den dout0 dout1 dout2 counter +* Bridge to digital +.model adc_buff adc_bridge(in_low = 0.8 in_high=2) +.model dac_buff dac_bridge(out_high = 3.5) +Aad [clk rst en] [dclk drst den] adc_buff +Ada [dout0 dout1 dout2] [out0 out1 out2] dac_buff + + +.tran 0.01 50 + +.control +run +plot v(clk) v(rst)+5 v(en)+10 v(out0)+20 v(out1)+25 v(out2)+30 +.endc + +.end diff --git a/misc/example.cc b/examples/cxx-api/demomain.cc index a8244ac4e..a64593306 100644 --- a/misc/example.cc +++ b/examples/cxx-api/demomain.cc @@ -1,6 +1,7 @@ -// clang -o example -std=c++11 -I/usr/include/tcl8.5 -I include/ example.cc objs/*.o -lstdc++ -lm -lrt -lreadline -lffi -ldl -ltcl8.5 +// Note: Set ENABLE_LIBYOSYS=1 in Makefile or Makefile.conf to build libyosys.so +// yosys-config --exec --cxx -o demomain --cxxflags --ldflags demomain.cc -lyosys -lstdc++ -#include <yosys.h> +#include <kernel/yosys.h> int main() { diff --git a/examples/cxx-api/evaldemo.cc b/examples/cxx-api/evaldemo.cc new file mode 100644 index 000000000..e5cc8d8e7 --- /dev/null +++ b/examples/cxx-api/evaldemo.cc @@ -0,0 +1,55 @@ +/* A simple Yosys plugin. (Copy&paste from http://stackoverflow.com/questions/32093541/how-does-the-yosys-consteval-api-work) + +Usage example: + +$ cat > evaldemo.v <<EOT +module main(input [1:0] A, input [7:0] B, C, D, output [7:0] Y); + assign Y = A == 0 ? B : A == 1 ? C : A == 2 ? D : 42; +endmodule +EOT + +$ yosys-config --build evaldemo.so evaldemo.cc +$ yosys -m evaldemo.so -p evaldemo evaldemo.v +*/ + +#include "kernel/yosys.h" +#include "kernel/consteval.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct EvalDemoPass : public Pass +{ + EvalDemoPass() : Pass("evaldemo") { } + + virtual void execute(vector<string>, Design *design) + { + Module *module = design->top_module(); + + if (module == nullptr) + log_error("No top module found!\n"); + + Wire *wire_a = module->wire("\\A"); + Wire *wire_y = module->wire("\\Y"); + + if (wire_a == nullptr) + log_error("No wire A found!\n"); + + if (wire_y == nullptr) + log_error("No wire Y found!\n"); + + ConstEval ce(module); + for (int v = 0; v < 4; v++) { + ce.push(); + ce.set(wire_a, Const(v, GetSize(wire_a))); + SigSpec sig_y = wire_y, sig_undef; + if (ce.eval(sig_y, sig_undef)) + log("Eval results for A=%d: Y=%s\n", v, log_signal(sig_y)); + else + log("Eval failed for A=%d: Missing value for %s\n", v, log_signal(sig_undef)); + ce.pop(); + } + } +} EvalDemoPass; + +PRIVATE_NAMESPACE_END diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 68b3327f9..57de725d8 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -2,11 +2,11 @@ * 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 @@ -30,15 +30,6 @@ #include "libs/sha1/sha1.h" #include "ast.h" -#include <sstream> -#include <stdarg.h> - -#if defined(__APPLE__) -# include <cmath> -#else -# include <math.h> -#endif - YOSYS_NAMESPACE_BEGIN using namespace AST; @@ -53,12 +44,12 @@ namespace AST { // instanciate global variables (private API) namespace AST_INTERNAL { - bool flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire; + bool flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire; AstNode *current_ast, *current_ast_mod; std::map<std::string, AstNode*> current_scope; const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL; RTLIL::SigSpec ignoreThisSignalsInInitial; - AstNode *current_top_block, *current_block, *current_block_child; + AstNode *current_always, *current_top_block, *current_block, *current_block_child; AstModule *current_module; } @@ -90,6 +81,7 @@ std::string AST::type2str(AstNodeType type) X(AST_IDENTIFIER) X(AST_PREFIX) X(AST_ASSERT) + X(AST_ASSUME) X(AST_FCALL) X(AST_TO_BITS) X(AST_TO_SIGNED) @@ -132,6 +124,7 @@ std::string AST::type2str(AstNodeType type) X(AST_TERNARY) X(AST_MEMRD) X(AST_MEMWR) + X(AST_MEMINIT) X(AST_TCALL) X(AST_ASSIGN) X(AST_CELL) @@ -144,6 +137,8 @@ std::string AST::type2str(AstNodeType type) X(AST_ASSIGN_LE) X(AST_CASE) X(AST_COND) + X(AST_CONDX) + X(AST_CONDZ) X(AST_DEFAULT) X(AST_FOR) X(AST_WHILE) @@ -156,6 +151,7 @@ std::string AST::type2str(AstNodeType type) X(AST_POSEDGE) X(AST_NEGEDGE) X(AST_EDGE) + X(AST_PACKAGE) #undef X default: log_abort(); @@ -327,7 +323,7 @@ static std::string id2vl(std::string txt) return txt; } -// dump AST node as verilog pseudo-code +// dump AST node as Verilog pseudo-code void AstNode::dumpVlog(FILE *f, std::string indent) { bool first = true; @@ -499,7 +495,12 @@ void AstNode::dumpVlog(FILE *f, std::string indent) break; case AST_CASE: - fprintf(f, "%s" "case (", indent.c_str()); + if (!children.empty() && children[0]->type == AST_CONDX) + fprintf(f, "%s" "casex (", indent.c_str()); + else if (!children.empty() && children[0]->type == AST_CONDZ) + fprintf(f, "%s" "casez (", indent.c_str()); + else + fprintf(f, "%s" "case (", indent.c_str()); children[0]->dumpVlog(f, ""); fprintf(f, ")\n"); for (size_t i = 1; i < children.size(); i++) { @@ -510,6 +511,8 @@ void AstNode::dumpVlog(FILE *f, std::string indent) break; case AST_COND: + case AST_CONDX: + case AST_CONDZ: for (auto child : children) { if (child->type == AST_BLOCK) { fprintf(f, ":\n"); @@ -553,7 +556,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) children[1]->dumpVlog(f, ""); fprintf(f, "}}"); break; - + if (0) { case AST_BIT_NOT: txt = "~"; } if (0) { case AST_REDUCE_AND: txt = "&"; } if (0) { case AST_REDUCE_OR: txt = "|"; } @@ -697,7 +700,7 @@ AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signe for (size_t i = 0; i < 32; i++) { if (i < node->bits.size()) node->integer |= (node->bits[i] == RTLIL::S1) << i; - else if (is_signed) + else if (is_signed && !node->bits.empty()) node->integer |= (node->bits.back() == RTLIL::S1) << i; } node->range_valid = true; @@ -818,7 +821,7 @@ uint64_t AstNode::asInt(bool is_signed) } if (type == AST_REALVALUE) - return realvalue; + return uint64_t(realvalue); log_abort(); } @@ -829,7 +832,7 @@ double AstNode::asReal(bool is_signed) { RTLIL::Const val(bits); - bool is_negative = is_signed && val.bits.back() == RTLIL::State::S1; + bool is_negative = is_signed && !val.bits.empty() && val.bits.back() == RTLIL::State::S1; if (is_negative) val = const_neg(val, val, false, false, val.bits.size()); @@ -892,7 +895,7 @@ static AstModule* process_module(AstNode *ast, bool defer) AstNode *ast_before_simplify = ast->clone(); if (flag_dump_ast1) { - log("Dumping verilog AST before simplification:\n"); + log("Dumping Verilog AST before simplification:\n"); ast->dumpAst(NULL, " "); log("--- END OF AST DUMP ---\n"); } @@ -902,13 +905,13 @@ static AstModule* process_module(AstNode *ast, bool defer) while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { } if (flag_dump_ast2) { - log("Dumping verilog AST after simplification:\n"); + log("Dumping Verilog AST after simplification:\n"); ast->dumpAst(NULL, " "); log("--- END OF AST DUMP ---\n"); } if (flag_dump_vlog) { - log("Dumping verilog AST (as requested by dump_vlog option):\n"); + log("Dumping Verilog AST (as requested by dump_vlog option):\n"); ast->dumpVlog(NULL, " "); log("--- END OF AST DUMP ---\n"); } @@ -957,6 +960,7 @@ static AstModule* process_module(AstNode *ast, bool defer) current_module->ast = ast_before_simplify; current_module->nolatches = flag_nolatches; + current_module->nomeminit = flag_nomeminit; current_module->nomem2reg = flag_nomem2reg; current_module->mem2reg = flag_mem2reg; current_module->lib = flag_lib; @@ -968,13 +972,14 @@ static AstModule* process_module(AstNode *ast, bool defer) } // create AstModule instances for all modules in the AST tree and add them to 'design' -void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire) +void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire) { current_ast = ast; flag_dump_ast1 = dump_ast1; flag_dump_ast2 = dump_ast2; flag_dump_vlog = dump_vlog; flag_nolatches = nolatches; + flag_nomeminit = nomeminit; flag_nomem2reg = nomem2reg; flag_mem2reg = mem2reg; flag_lib = lib; @@ -992,6 +997,14 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump for (auto n : global_decls) (*it)->children.push_back(n->clone()); + for (auto n : design->verilog_packages){ + for (auto o : n->children) { + AstNode *cloned_node = o->clone(); + cloned_node->str = n->str + std::string("::") + cloned_node->str.substr(1); + (*it)->children.push_back(cloned_node); + } + } + if (flag_icells && (*it)->str.substr(0, 2) == "\\$") (*it)->str = (*it)->str.substr(1); @@ -1009,6 +1022,9 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump design->add(process_module(*it, defer)); } + else if ((*it)->type == AST_PACKAGE){ + design->verilog_packages.push_back((*it)->clone()); + } else global_decls.push_back(*it); } @@ -1029,13 +1045,14 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R if (stripped_name.substr(0, 9) == "$abstract") stripped_name = stripped_name.substr(9); - log_header("Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str()); + log_header(design, "Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str()); current_ast = NULL; flag_dump_ast1 = false; flag_dump_ast2 = false; flag_dump_vlog = false; flag_nolatches = nolatches; + flag_nomeminit = nomeminit; flag_nomem2reg = nomem2reg; flag_mem2reg = mem2reg; flag_lib = lib; @@ -1102,6 +1119,7 @@ RTLIL::Module *AstModule::clone() const new_mod->ast = ast->clone(); new_mod->nolatches = nolatches; + new_mod->nomeminit = nomeminit; new_mod->nomem2reg = nomem2reg; new_mod->mem2reg = mem2reg; new_mod->lib = lib; diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 180646267..3dcd32bd4 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -2,11 +2,11 @@ * 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 @@ -64,6 +64,7 @@ namespace AST AST_IDENTIFIER, AST_PREFIX, AST_ASSERT, + AST_ASSUME, AST_FCALL, AST_TO_BITS, @@ -107,6 +108,7 @@ namespace AST AST_TERNARY, AST_MEMRD, AST_MEMWR, + AST_MEMINIT, AST_TCALL, AST_ASSIGN, @@ -120,6 +122,8 @@ namespace AST AST_ASSIGN_LE, AST_CASE, AST_COND, + AST_CONDX, + AST_CONDZ, AST_DEFAULT, AST_FOR, AST_WHILE, @@ -133,7 +137,9 @@ namespace AST AST_POSEDGE, AST_NEGEDGE, - AST_EDGE + AST_EDGE, + + AST_PACKAGE }; // convert an node type to a string (e.g. for debug output) @@ -208,13 +214,14 @@ namespace AST // simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc. // it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL() bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param); - AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr); + AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init); void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map); void replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules); void mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places, dict<AstNode*, uint32_t> &mem2reg_flags, dict<AstNode*, uint32_t> &proc_flags, uint32_t &status_flags); - void mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block); + bool mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block); bool mem2reg_check(pool<AstNode*> &mem2reg_set); + void mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes); void meminfo(int &mem_width, int &mem_size, int &addr_bits); // additional functionality for evaluating constant functions @@ -264,13 +271,13 @@ namespace AST }; // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code - void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire); + void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire); // parametric modules are supported directly by the AST library - // therfore we need our own derivate of RTLIL::Module with overloaded virtual functions + // therefore we need our own derivate of RTLIL::Module with overloaded virtual functions struct AstModule : RTLIL::Module { AstNode *ast; - bool nolatches, nomem2reg, mem2reg, lib, noopt, icells, autowire; + bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire; virtual ~AstModule(); virtual RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters); virtual RTLIL::Module *clone() const; @@ -294,12 +301,12 @@ namespace AST namespace AST_INTERNAL { // internal state variables - extern bool flag_dump_ast1, flag_dump_ast2, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire; + extern bool flag_dump_ast1, flag_dump_ast2, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire; extern AST::AstNode *current_ast, *current_ast_mod; extern std::map<std::string, AST::AstNode*> current_scope; extern const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr; extern RTLIL::SigSpec ignoreThisSignalsInInitial; - extern AST::AstNode *current_top_block, *current_block, *current_block_child; + extern AST::AstNode *current_always, *current_top_block, *current_block, *current_block_child; extern AST::AstModule *current_module; struct ProcessGenerator; } diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc index e566d653d..e241142d3 100644 --- a/frontends/ast/dpicall.cc +++ b/frontends/ast/dpicall.cc @@ -2,11 +2,11 @@ * 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 diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 71248663e..3e359170b 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -2,11 +2,11 @@ * 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 @@ -176,13 +176,13 @@ struct AST_INTERNAL::ProcessGenerator RTLIL::Process *proc; RTLIL::SigSpec outputSignals; - // This always points to the RTLIL::CaseRule beeing filled at the moment + // This always points to the RTLIL::CaseRule being filled at the moment RTLIL::CaseRule *current_case; // This map contains the replacement pattern to be used in the right hand side // of an assignment. E.g. in the code "foo = bar; foo = func(foo);" the foo in the right // hand side of the 2nd assignment needs to be replace with the temporary signal holding - // the value assigned in the first assignment. So when the first assignement is processed + // the value assigned in the first assignment. So when the first assignment is processed // the according information is appended to subst_rvalue_from and subst_rvalue_to. stackmap<RTLIL::SigBit, RTLIL::SigBit> subst_rvalue_map; @@ -192,7 +192,7 @@ struct AST_INTERNAL::ProcessGenerator // signal that is used as input for the register that drives the signal foo. stackmap<RTLIL::SigBit, RTLIL::SigBit> subst_lvalue_map; - // The code here generates a number of temprorary signal for each output register. This + // The code here generates a number of temporary signal for each output register. This // map helps generating nice numbered names for all this temporary signals. std::map<RTLIL::Wire*, int> new_temp_count; @@ -338,12 +338,14 @@ struct AST_INTERNAL::ProcessGenerator case AST_CASE: for (auto child : ast->children) if (child != ast->children[0]) { - log_assert(child->type == AST_COND); + log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ); collect_lvalues(reg, child, type_eq, type_le, false); } break; case AST_COND: + case AST_CONDX: + case AST_CONDZ: case AST_ALWAYS: case AST_INITIAL: for (auto child : ast->children) @@ -379,7 +381,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); @@ -427,6 +429,17 @@ struct AST_INTERNAL::ProcessGenerator { RTLIL::SigSpec unmapped_lvalue = ast->children[0]->genRTLIL(), lvalue = unmapped_lvalue; RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.size(), &subst_rvalue_map.stdmap()); + + pool<SigBit> lvalue_sigbits; + for (int i = 0; i < GetSize(lvalue); i++) { + if (lvalue_sigbits.count(lvalue[i]) > 0) { + unmapped_lvalue.remove(i); + lvalue.remove(i); + rvalue.remove(i--); + } else + lvalue_sigbits.insert(lvalue[i]); + } + lvalue.replace(subst_lvalue_map.stdmap()); if (ast->type == AST_ASSIGN_EQ) { @@ -434,7 +447,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)); } @@ -467,7 +480,7 @@ struct AST_INTERNAL::ProcessGenerator { if (child == ast->children[0]) continue; - log_assert(child->type == AST_COND); + log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ); subst_lvalue_map.save(); subst_rvalue_map.save(); @@ -511,7 +524,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 +533,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 +565,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: @@ -567,9 +585,11 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (id_ast->children.size() > 1 && id_ast->children[1]->range_valid) { this_width = id_ast->children[1]->range_left - id_ast->children[1]->range_right + 1; } else - if (id_ast->children[0]->type == AST_CONSTANT) { + if (id_ast->children[0]->type != AST_CONSTANT) + while (id_ast->simplify(true, false, false, 1, -1, false, true)) { } + if (id_ast->children[0]->type == AST_CONSTANT) this_width = id_ast->children[0]->bits.size(); - } else + else log_error("Failed to detect width for parameter %s at %s:%d!\n", str.c_str(), filename.c_str(), linenum); if (children.size() != 0) range = children[0]; @@ -582,7 +602,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun // log("---\n"); // id_ast->dumpAst(NULL, "decl> "); // dumpAst(NULL, "ref> "); - log_error("Failed to detect with of signal access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); + log_error("Failed to detect width of signal access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); } } else { this_width = id_ast->range_left - id_ast->range_right + 1; @@ -593,7 +613,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun this_width = 32; } else if (id_ast->type == AST_MEMORY) { if (!id_ast->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); + log_error("Failed to detect width of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1; } else log_error("Failed to detect width for identifier %s at %s:%d!\n", str.c_str(), filename.c_str(), linenum); @@ -615,7 +635,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; @@ -625,7 +645,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: @@ -644,7 +664,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; @@ -653,7 +673,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; @@ -676,7 +696,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; @@ -696,7 +716,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; @@ -712,7 +732,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; @@ -725,9 +745,9 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (!id2ast->is_signed) sign_hint = false; 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); + log_error("Failed to detect width 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. @@ -764,7 +784,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // in the following big switch() statement there are some uses of // Clifford's Device (http://www.clifford.at/cfun/cliffdev/). In this // cases this variable is used to hold the type of the cell that should - // be instanciated for this type of AST node. + // be instantiated for this type of AST node. std::string type_name; current_filename = filename; @@ -773,7 +793,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) switch (type) { // simply ignore this nodes. - // they are eighter leftovers from simplify() or are referenced by other nodes + // they are either leftovers from simplify() or are referenced by other nodes // and are only accessed here thru this references case AST_TASK: case AST_FUNCTION: @@ -786,6 +806,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_GENBLOCK: case AST_GENIF: case AST_GENCASE: + case AST_PACKAGE: break; // remember the parameter, needed for example in techmap @@ -1052,7 +1073,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; @@ -1066,16 +1087,16 @@ 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; } // generate cells for unary operations: $reduce_bool - // (this is actually just an $reduce_or, but for clearity a different cell type is used) + // (this is actually just an $reduce_or, but for clarity a different cell type is used) 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; } @@ -1121,7 +1142,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); @@ -1143,7 +1164,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) { @@ -1152,10 +1173,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); @@ -1167,14 +1188,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) @@ -1190,7 +1211,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); @@ -1214,11 +1235,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_DATA", current_module->memories[str]->width); wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); - int addr_bits = 1; - while ((1 << addr_bits) < current_module->memories[str]->size) - addr_bits++; + int mem_width, mem_size, addr_bits; + id2ast->meminfo(mem_width, mem_size, addr_bits); cell->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::Sx, 1)); + cell->setPort("\\EN", RTLIL::SigSpec(RTLIL::State::Sx, 1)); cell->setPort("\\ADDR", children[0]->genWidthRTLIL(addr_bits)); cell->setPort("\\DATA", RTLIL::SigSpec(wire)); @@ -1235,28 +1256,38 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // generate $memwr cells for memory write ports case AST_MEMWR: + case AST_MEMINIT: { std::stringstream sstr; - sstr << "$memwr$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++); + sstr << (type == AST_MEMWR ? "$memwr$" : "$meminit$") << str << "$" << filename << ":" << linenum << "$" << (autoidx++); - RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$memwr"); + RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_MEMWR ? "$memwr" : "$meminit"); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); - int addr_bits = 1; - while ((1 << addr_bits) < current_module->memories[str]->size) - addr_bits++; + int mem_width, mem_size, addr_bits; + id2ast->meminfo(mem_width, mem_size, addr_bits); + + int num_words = 1; + 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 = int(children[2]->asInt(false)); + cell->parameters["\\WORDS"] = RTLIL::Const(num_words); + } - cell->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::Sx, 1)); cell->setPort("\\ADDR", children[0]->genWidthRTLIL(addr_bits)); - cell->setPort("\\DATA", children[1]->genWidthRTLIL(current_module->memories[str]->width)); - cell->setPort("\\EN", children[2]->genRTLIL()); + cell->setPort("\\DATA", children[1]->genWidthRTLIL(current_module->memories[str]->width * num_words)); cell->parameters["\\MEMID"] = RTLIL::Const(str); cell->parameters["\\ABITS"] = RTLIL::Const(addr_bits); cell->parameters["\\WIDTH"] = RTLIL::Const(current_module->memories[str]->width); - cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(0); - cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0); + if (type == AST_MEMWR) { + cell->setPort("\\CLK", RTLIL::SigSpec(RTLIL::State::Sx, 1)); + cell->setPort("\\EN", children[2]->genRTLIL()); + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(0); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0); + } cell->parameters["\\PRIORITY"] = RTLIL::Const(autoidx-1); } @@ -1264,19 +1295,22 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // generate $assert cells case AST_ASSERT: + case AST_ASSUME: { log_assert(children.size() == 2); RTLIL::SigSpec check = children[0]->genRTLIL(); - log_assert(check.size() == 1); + if (GetSize(check) != 1) + check = current_module->ReduceBool(NEW_ID, check); RTLIL::SigSpec en = children[1]->genRTLIL(); - log_assert(en.size() == 1); + if (GetSize(en) != 1) + en = current_module->ReduceBool(NEW_ID, en); std::stringstream sstr; - sstr << "$assert$" << filename << ":" << linenum << "$" << (autoidx++); + sstr << (type == AST_ASSERT ? "$assert$" : "$assume$") << filename << ":" << linenum << "$" << (autoidx++); - RTLIL::Cell *cell = current_module->addCell(sstr.str(), "$assert"); + RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_ASSERT ? "$assert" : "$assume"); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); for (auto &attr : attributes) { @@ -1335,16 +1369,19 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) continue; } if (child->type == AST_PARASET) { - if (child->children[0]->type != AST_CONSTANT) - log_error("Parameter `%s' with non-constant value at %s:%d!\n", - child->str.c_str(), filename.c_str(), linenum); - if (child->str.size() == 0) { - char buf[100]; - snprintf(buf, 100, "$%d", ++para_counter); - cell->parameters[buf] = child->children[0]->asParaConst(); - } else { - cell->parameters[child->str] = child->children[0]->asParaConst(); + IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str; + if (child->children[0]->type == AST_REALVALUE) { + log_warning("Replacing floating point parameter %s.%s = %f with string at %s:%d.\n", + log_id(cell), log_id(paraname), child->children[0]->realvalue, + filename.c_str(), linenum); + auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue)); + strnode->cloneInto(child->children[0]); + delete strnode; } + if (child->children[0]->type != AST_CONSTANT) + log_error("Parameter %s.%s with non-constant value at %s:%d!\n", + log_id(cell), log_id(paraname), filename.c_str(), linenum); + cell->parameters[paraname] = child->children[0]->asParaConst(); continue; } if (child->type == AST_ARGUMENT) { @@ -1398,7 +1435,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } // this is a wrapper for AstNode::genRTLIL() when a specific signal width is requested and/or -// signals must be substituted before beeing used as input values (used by ProcessGenerator) +// signals must be substituted before being used as input values (used by ProcessGenerator) // note that this is using some global variables to communicate this special settings to AstNode::genRTLIL(). RTLIL::SigSpec AstNode::genWidthRTLIL(int width, const dict<RTLIL::SigBit, RTLIL::SigBit> *new_subst_ptr) { diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index e9750eba6..c09b912c2 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -2,11 +2,11 @@ * 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 @@ -41,7 +41,7 @@ YOSYS_NAMESPACE_BEGIN using namespace AST; using namespace AST_INTERNAL; -// convert the AST into a simpler AST that has all parameters subsitited by their +// convert the AST into a simpler AST that has all parameters substituted by their // values, unrolled for-loops, expanded generate blocks, etc. when this function // is done with an AST it can be converted into RTLIL using genRTLIL(). // @@ -49,15 +49,24 @@ using namespace AST_INTERNAL; // nodes that link to a different node using names and lexical scoping. bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param) { + static int recursion_counter = 0; + static pair<string, int> last_blocking_assignment_warn; + static bool deep_recursion_warning = false; + + if (recursion_counter++ == 1000 && deep_recursion_warning) { + log_warning("Deep recursion in AST simplifier.\nDoes this design contain insanely long expressions?\n"); + deep_recursion_warning = false; + } + AstNode *newNode = NULL; bool did_something = false; - static pair<string, int> last_blocking_assignment_warn; #if 0 log("-------------\n"); + log("AST simplify[%d] depth %d at %s:%d,\n", stage, recursion_counter, filename.c_str(), linenum); log("const_fold=%d, at_zero=%d, in_lvalue=%d, stage=%d, width_hint=%d, sign_hint=%d, in_param=%d\n", int(const_fold), int(at_zero), int(in_lvalue), int(stage), int(width_hint), int(sign_hint), int(in_param)); - dumpAst(NULL, "> "); + // dumpAst(NULL, "> "); #endif if (stage == 0) @@ -65,6 +74,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, log_assert(type == AST_MODULE); last_blocking_assignment_warn = pair<string, int>(); + deep_recursion_warning = true; while (simplify(const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint, in_param)) { } if (!flag_nomem2reg && !get_bool_attribute("\\nomem2reg")) @@ -79,11 +89,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, { AstNode *mem = it.first; uint32_t memflags = it.second; + bool this_nomeminit = flag_nomeminit; log_assert((memflags & ~0x00ffff00) == 0); if (mem->get_bool_attribute("\\nomem2reg")) continue; + if (mem->get_bool_attribute("\\nomeminit") || get_bool_attribute("\\nomeminit")) + this_nomeminit = true; + if (memflags & AstNode::MEM2REG_FL_FORCED) goto silent_activate; @@ -93,7 +107,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (memflags & AstNode::MEM2REG_FL_SET_ASYNC) goto verbose_activate; - if ((memflags & AstNode::MEM2REG_FL_SET_INIT) && (memflags & AstNode::MEM2REG_FL_SET_ELSE)) + if ((memflags & AstNode::MEM2REG_FL_SET_INIT) && (memflags & AstNode::MEM2REG_FL_SET_ELSE) && this_nomeminit) goto verbose_activate; if (memflags & AstNode::MEM2REG_FL_CMPLX_LHS) @@ -134,17 +148,17 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } - mem2reg_as_needed_pass2(mem2reg_set, this, NULL); + while (mem2reg_as_needed_pass2(mem2reg_set, this, NULL)) { } - for (size_t i = 0; i < children.size(); i++) { - if (mem2reg_set.count(children[i]) > 0) { - delete children[i]; - children.erase(children.begin() + (i--)); - } - } + vector<AstNode*> delnodes; + mem2reg_remove(mem2reg_set, delnodes); + + for (auto node : delnodes) + delete node; } while (simplify(const_fold, at_zero, in_lvalue, 2, width_hint, sign_hint, in_param)) { } + recursion_counter--; return false; } @@ -152,18 +166,144 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, set_line_num(linenum); // we do not look inside a task or function - // (but as soon as a task of function is instanciated we process the generated AST as usual) - if (type == AST_FUNCTION || type == AST_TASK) + // (but as soon as a task or function is instantiated we process the generated AST as usual) + if (type == AST_FUNCTION || type == AST_TASK) { + recursion_counter--; return false; + } - // deactivate all calls to non-synthesis system taks - if ((type == AST_FCALL || type == AST_TCALL) && (str == "$display" || str == "$strobe" || str == "$monitor" || str == "$time" || str == "$stop" || str == "$finish" || + // deactivate all calls to non-synthesis system tasks + // note that $display, $finish, and $stop are used for synthesis-time DRC so they're not in this list + if ((type == AST_FCALL || type == AST_TCALL) && (str == "$strobe" || str == "$monitor" || str == "$time" || str == "$dumpfile" || str == "$dumpvars" || str == "$dumpon" || str == "$dumpoff" || str == "$dumpall")) { log_warning("Ignoring call to system %s %s at %s:%d.\n", type == AST_FCALL ? "function" : "task", str.c_str(), filename.c_str(), linenum); delete_children(); str = std::string(); } + if ((type == AST_TCALL) && (str == "$display" || str == "$write") && (!current_always || current_always->type != AST_INITIAL)) { + log_warning("System task `%s' outside initial block is unsupported at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + delete_children(); + str = std::string(); + } + + // print messages if this a call to $display() or $write() + // This code implements only a small subset of Verilog-2005 $display() format specifiers, + // but should be good enough for most uses + if ((type == AST_TCALL) && ((str == "$display") || (str == "$write"))) + { + int nargs = GetSize(children); + if (nargs < 1) + log_error("System task `%s' got %d arguments, expected >= 1 at %s:%d.\n", + str.c_str(), int(children.size()), filename.c_str(), linenum); + + // First argument is the format string + AstNode *node_string = children[0]; + while (node_string->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_string->type != AST_CONSTANT) + log_error("Failed to evaluate system task `%s' with non-constant 1st argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + std::string sformat = node_string->bitsAsConst().decode_string(); + + // Other arguments are placeholders. Process the string as we go through it + std::string sout; + int next_arg = 1; + for (size_t i = 0; i < sformat.length(); i++) + { + // format specifier + if (sformat[i] == '%') + { + // If there's no next character, that's a problem + if (i+1 >= sformat.length()) + log_error("System task `%s' called with `%%' at end of string at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + + char cformat = sformat[++i]; + + // %% is special, does not need a matching argument + if (cformat == '%') + { + sout += '%'; + continue; + } + + // Simplify the argument + AstNode *node_arg = nullptr; + + // Everything from here on depends on the format specifier + switch (cformat) + { + case 's': + case 'S': + case 'd': + case 'D': + case 'x': + case 'X': + if (next_arg >= GetSize(children)) + log_error("Missing argument for %%%c format specifier in system task `%s' at %s:%d.\n", + cformat, str.c_str(), filename.c_str(), linenum); + + node_arg = children[next_arg++]; + while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_arg->type != AST_CONSTANT) + log_error("Failed to evaluate system task `%s' with non-constant argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + break; + + case 'm': + case 'M': + break; + + default: + log_error("System task `%s' called with invalid/unsupported format specifier at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + break; + } + + switch (cformat) + { + case 's': + case 'S': + sout += node_arg->bitsAsConst().decode_string(); + break; + + case 'd': + case 'D': + { + char tmp[128]; + snprintf(tmp, sizeof(tmp), "%d", node_arg->bitsAsConst().as_int()); + sout += tmp; + } + break; + + case 'x': + case 'X': + { + char tmp[128]; + snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int()); + sout += tmp; + } + break; + + case 'm': + case 'M': + sout += log_id(current_module->name); + break; + + default: + log_abort(); + } + } + + // not a format specifier + else + sout += sformat[i]; + } + + // Finally, print the message (only include a \n for $display, not for $write) + log("%s", sout.c_str()); + if (str == "$display") + log("\n"); + delete_children(); + str = std::string(); + } + // activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.) if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX) const_fold = true; @@ -255,6 +395,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, auto backup_current_block = current_block; auto backup_current_block_child = current_block_child; auto backup_current_top_block = current_top_block; + auto backup_current_always = current_always; + + if (type == AST_ALWAYS || type == AST_INITIAL) + current_always = this; int backup_width_hint = width_hint; bool backup_sign_hint = sign_hint; @@ -277,7 +421,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; @@ -291,7 +435,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; @@ -362,7 +506,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, detect_width_simple = true; child_0_is_self_determined = true; break; - + case AST_MEMRD: detect_width_simple = true; children_are_self_determined = true; @@ -395,6 +539,18 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } + if (type == AST_CONDX && children.size() > 0 && children.at(0)->type == AST_CONSTANT) { + for (auto &bit : children.at(0)->bits) + if (bit == State::Sz || bit == State::Sx) + bit = State::Sa; + } + + if (type == AST_CONDZ && children.size() > 0 && children.at(0)->type == AST_CONSTANT) { + for (auto &bit : children.at(0)->bits) + if (bit == State::Sz) + bit = State::Sa; + } + if (const_fold && type == AST_CASE) { while (children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { } @@ -403,7 +559,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, new_children.push_back(children[0]); for (int i = 1; i < GetSize(children); i++) { AstNode *child = children[i]; - log_assert(child->type == AST_COND); + log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ); for (auto v : child->children) { if (v->type == AST_DEFAULT) goto keep_const_cond; @@ -494,6 +650,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, current_block = backup_current_block; current_block_child = backup_current_block_child; current_top_block = backup_current_top_block; + current_always = backup_current_always; for (auto it = backup_scope.begin(); it != backup_scope.end(); it++) { if (it->second == NULL) @@ -530,6 +687,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // dumpAst(NULL, "> "); log_error("Index in generate block prefix syntax at %s:%d is not constant!\n", filename.c_str(), linenum); } + if (children[1]->type == AST_PREFIX) + children[1]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param); log_assert(children[1]->type == AST_IDENTIFIER); newNode = children[1]->clone(); const char *second_part = children[1]->str.c_str(); @@ -609,8 +768,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]; @@ -636,10 +795,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (i == 0) index_expr = new_index_expr; else - index_expr = new AstNode(AST_ADD, new AstNode(AST_MUL, index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i-1], true)), new_index_expr); + index_expr = new AstNode(AST_ADD, new AstNode(AST_MUL, index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i+1], true)), new_index_expr); } - for (int i = GetSize(id2ast->multirange_dimensions)/1; i < GetSize(children[0]->children); i++) + for (int i = GetSize(id2ast->multirange_dimensions)/2; i < GetSize(children[0]->children); i++) children.push_back(children[0]->children[i]->clone()); delete children[0]; @@ -656,7 +815,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (children.size() > 1 && children[1]->type == AST_RANGE) { if (!children[1]->range_valid) log_error("Non-constant width range on parameter decl at %s:%d.\n", filename.c_str(), linenum); - int width = children[1]->range_left - children[1]->range_right + 1; + int width = std::abs(children[1]->range_left - children[1]->range_right) + 1; if (children[0]->type == AST_REALVALUE) { RTLIL::Const constvalue = children[0]->realAsConst(width); log_warning("converting real value %e to binary %s at %s:%d.\n", @@ -670,7 +829,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, RTLIL::SigSpec sig(children[0]->bits); sig.extend_u0(width, children[0]->is_signed); AstNode *old_child_0 = children[0]; - children[0] = mkconst_bits(sig.as_const().bits, children[0]->is_signed); + children[0] = mkconst_bits(sig.as_const().bits, is_signed); delete old_child_0; } children[0]->is_signed = is_signed; @@ -803,7 +962,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // eval 1st expression AstNode *varbuf = init_ast->children[1]->clone(); - while (varbuf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (varbuf->simplify(true, false, false, stage, 32, true, false)) { } if (varbuf->type != AST_CONSTANT) log_error("Right hand side of 1st expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum); @@ -866,7 +1025,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // eval 3rd expression buf = next_ast->children[1]->clone(); - while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (buf->simplify(true, false, false, stage, 32, true, false)) { } if (buf->type != AST_CONSTANT) log_error("Right hand side of 3rd expression of generate for-loop at %s:%d is not constant!\n", filename.c_str(), linenum); @@ -889,7 +1048,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]; @@ -977,7 +1136,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, AstNode *selected_case = NULL; for (size_t i = 1; i < children.size(); i++) { - log_assert(children.at(i)->type == AST_COND); + log_assert(children.at(i)->type == AST_COND || children.at(i)->type == AST_CONDX || children.at(i)->type == AST_CONDZ); AstNode *this_genblock = NULL; for (auto child : children.at(i)->children) { @@ -1045,7 +1204,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; @@ -1063,7 +1222,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, goto apply_newNode; } - // replace primitives with assignmens + // replace primitives with assignments if (type == AST_PRIMITIVE) { if (children.size() < 2) @@ -1189,7 +1348,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } skip_dynamic_range_lvalue_expansion:; - if (stage > 1 && type == AST_ASSERT && current_block != NULL) + if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME) && current_block != NULL) { std::stringstream sstr; sstr << "$assert$" << filename << ":" << linenum << "$" << (autoidx++); @@ -1233,7 +1392,7 @@ skip_dynamic_range_lvalue_expansion:; newNode->children.push_back(assign_check); newNode->children.push_back(assign_en); - AstNode *assertnode = new AstNode(AST_ASSERT); + AstNode *assertnode = new AstNode(type); assertnode->children.push_back(new AstNode(AST_IDENTIFIER)); assertnode->children.push_back(new AstNode(AST_IDENTIFIER)); assertnode->children[0]->str = id_check; @@ -1244,16 +1403,15 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } - if (stage > 1 && type == AST_ASSERT && children.size() == 1) + if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME) && children.size() == 1) { - children[0] = new AstNode(AST_REDUCE_BOOL, children[0]->clone()); children.push_back(mkconst_int(1, false, 1)); did_something = true; } // found right-hand side identifier for memory -> replace with memory read port if (stage > 1 && type == AST_IDENTIFIER && id2ast != NULL && id2ast->type == AST_MEMORY && !in_lvalue && - children[0]->type == AST_RANGE && children[0]->children.size() == 1) { + children.size() == 1 && children[0]->type == AST_RANGE && children[0]->children.size() == 1) { newNode = new AstNode(AST_MEMRD, children[0]->children[0]->clone()); newNode->str = str; newNode->id2ast = id2ast; @@ -1293,11 +1451,14 @@ skip_dynamic_range_lvalue_expansion:; current_scope[wire_data->str] = wire_data; while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } - AstNode *wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); - wire_en->str = id_en; - current_ast_mod->children.push_back(wire_en); - current_scope[wire_en->str] = wire_en; - while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } + AstNode *wire_en = nullptr; + if (current_always->type != AST_INITIAL) { + wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); + wire_en->str = id_en; + current_ast_mod->children.push_back(wire_en); + current_scope[wire_en->str] = wire_en; + while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } + } std::vector<RTLIL::State> x_bits_addr, x_bits_data, set_bits_en; for (int i = 0; i < addr_bits; i++) @@ -1313,13 +1474,17 @@ skip_dynamic_range_lvalue_expansion:; AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false)); assign_data->children[0]->str = id_data; - AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width)); - assign_en->children[0]->str = id_en; + AstNode *assign_en = nullptr; + if (current_always->type != AST_INITIAL) { + assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width)); + assign_en->children[0]->str = id_en; + } AstNode *default_signals = new AstNode(AST_BLOCK); default_signals->children.push_back(assign_addr); default_signals->children.push_back(assign_data); - default_signals->children.push_back(assign_en); + if (current_always->type != AST_INITIAL) + default_signals->children.push_back(assign_en); current_top_block->children.insert(current_top_block->children.begin(), default_signals); assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); @@ -1334,15 +1499,16 @@ skip_dynamic_range_lvalue_expansion:; std::vector<RTLIL::State> padding_x(offset, RTLIL::State::Sx); - for (int i = 0; i < mem_width; i++) - set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0; - assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_CONCAT, mkconst_bits(padding_x, false), children[1]->clone())); assign_data->children[0]->str = id_data; - assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); - assign_en->children[0]->str = id_en; + if (current_always->type != AST_INITIAL) { + for (int i = 0; i < mem_width; i++) + set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0; + assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); + assign_en->children[0]->str = id_en; + } } else { @@ -1357,16 +1523,17 @@ skip_dynamic_range_lvalue_expansion:; log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1; - for (int i = 0; i < mem_width; i++) - set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0; - assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone())); assign_data->children[0]->str = id_data; - assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), - new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone())); - assign_en->children[0]->str = id_en; + if (current_always->type != AST_INITIAL) { + for (int i = 0; i < mem_width; i++) + set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0; + assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), + new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone())); + assign_en->children[0]->str = id_en; + } delete left_at_zero_ast; delete right_at_zero_ast; @@ -1378,23 +1545,31 @@ skip_dynamic_range_lvalue_expansion:; assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone()); assign_data->children[0]->str = id_data; - assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); - assign_en->children[0]->str = id_en; + if (current_always->type != AST_INITIAL) { + assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); + assign_en->children[0]->str = id_en; + } } newNode = new AstNode(AST_BLOCK); newNode->children.push_back(assign_addr); newNode->children.push_back(assign_data); - newNode->children.push_back(assign_en); + if (current_always->type != AST_INITIAL) + newNode->children.push_back(assign_en); - AstNode *wrnode = new AstNode(AST_MEMWR); - wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); + AstNode *wrnode = new AstNode(current_always->type == AST_INITIAL ? AST_MEMINIT : AST_MEMWR); wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); + if (current_always->type != AST_INITIAL) + wrnode->children.push_back(new AstNode(AST_IDENTIFIER)); + else + wrnode->children.push_back(AstNode::mkconst_int(1, false)); wrnode->str = children[0]->str; + wrnode->id2ast = children[0]->id2ast; wrnode->children[0]->str = id_addr; wrnode->children[1]->str = id_data; - wrnode->children[2]->str = id_en; + if (current_always->type != AST_INITIAL) + wrnode->children[2]->str = id_en; current_ast_mod->children.push_back(wrnode); goto apply_newNode; @@ -1531,7 +1706,17 @@ skip_dynamic_range_lvalue_expansion:; if (current_scope.count(str) == 0 || current_scope[str]->type != AST_FUNCTION) log_error("Can't resolve function name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum); } - if (type == AST_TCALL) { + + if (type == AST_TCALL) + { + if (str == "$finish" || str == "$stop") + { + if (!current_always || current_always->type != AST_INITIAL) + log_error("System task `%s' outside initial block is unsupported at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + + log_error("System task `%s' executed at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + } + if (str == "\\$readmemh" || str == "\\$readmemb") { if (GetSize(children) < 2 || GetSize(children) > 4) @@ -1555,7 +1740,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) { @@ -1563,10 +1748,27 @@ 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)); } - newNode = readmem(str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr); + bool unconditional_init = false; + if (current_always->type == AST_INITIAL) { + pool<AstNode*> queue; + log_assert(current_always->children[0]->type == AST_BLOCK); + queue.insert(current_always->children[0]); + while (!unconditional_init && !queue.empty()) { + pool<AstNode*> next_queue; + for (auto n : queue) + for (auto c : n->children) { + if (c == this) + unconditional_init = true; + next_queue.insert(c); + } + next_queue.swap(queue); + } + } + + newNode = readmem(str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr, unconditional_init); goto apply_newNode; } @@ -1606,6 +1808,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) { @@ -1698,17 +1902,41 @@ 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; + if (!child->is_output) + wire->attributes["\\nosync"] = AstNode::mkconst_int(1, 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()) { @@ -1728,8 +1956,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); @@ -1876,7 +2109,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); @@ -2036,6 +2269,7 @@ apply_newNode: if (!did_something) basic_prep = true; + recursion_counter--; return did_something; } @@ -2048,10 +2282,18 @@ static void replace_result_wire_name_in_function(AstNode *node, std::string &fro } // replace a readmem[bh] TCALL ast node with a block of memory assignments -AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr) +AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init) { + int mem_width, mem_size, addr_bits; + memory->meminfo(mem_width, mem_size, addr_bits); + AstNode *block = new AstNode(AST_BLOCK); + AstNode *meminit = nullptr; + int next_meminit_cursor=0; + vector<State> meminit_bits; + int meminit_size=0; + std::ifstream f; f.open(mem_filename.c_str()); @@ -2060,13 +2302,13 @@ 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; if (finish_addr < 0) - finish_addr = range_max; + finish_addr = range_max + 1; bool in_comment = false; int increment = start_addr <= finish_addr ? +1 : -1; @@ -2106,21 +2348,56 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m continue; } - AstNode *value = VERILOG_FRONTEND::const2ast((is_readmemh ? "'h" : "'b") + token); + AstNode *value = VERILOG_FRONTEND::const2ast(stringf("%d'%c", mem_width, is_readmemh ? 'h' : 'b') + token); + + if (unconditional_init) + { + if (meminit == nullptr || cursor != next_meminit_cursor) + { + if (meminit != nullptr) { + meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false); + meminit->children[2] = AstNode::mkconst_int(meminit_size, false); + } + + meminit = new AstNode(AST_MEMINIT); + meminit->children.push_back(AstNode::mkconst_int(cursor, false)); + meminit->children.push_back(nullptr); + meminit->children.push_back(nullptr); + meminit->str = memory->str; + meminit->id2ast = memory; + meminit_bits.clear(); + meminit_size = 0; + + current_ast_mod->children.push_back(meminit); + next_meminit_cursor = cursor; + } - block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor, false))), value)); - block->children.back()->children[0]->str = memory->str; - block->children.back()->children[0]->id2ast = memory; + meminit_size++; + next_meminit_cursor++; + meminit_bits.insert(meminit_bits.end(), value->bits.begin(), value->bits.end()); + delete value; + } + else + { + block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor, false))), value)); + block->children.back()->children[0]->str = memory->str; + block->children.back()->children[0]->id2ast = memory; + } - if ((cursor == finish_addr) || (increment > 0 && cursor >= range_max) || (increment < 0 && cursor <= range_min)) + if ((cursor == finish_addr) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min)) break; cursor += increment; } - if ((cursor == finish_addr) || (increment > 0 && cursor >= range_max) || (increment < 0 && cursor <= range_min)) + if ((cursor == finish_addr) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min)) break; } + if (meminit != nullptr) { + meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false); + meminit->children[2] = AstNode::mkconst_int(meminit_size, false); + } + return block; } @@ -2171,7 +2448,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma name_map.swap(backup_name_map); } -// rename stuff (used when tasks of functions are instanciated) +// rename stuff (used when tasks of functions are instantiated) void AstNode::replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules) { if (type == AST_BLOCK) @@ -2328,9 +2605,28 @@ bool AstNode::mem2reg_check(pool<AstNode*> &mem2reg_set) return true; } +void AstNode::mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes) +{ + log_assert(mem2reg_set.count(this) == 0); + + if (mem2reg_set.count(id2ast)) + id2ast = nullptr; + + for (size_t i = 0; i < children.size(); i++) { + if (mem2reg_set.count(children[i]) > 0) { + delnodes.push_back(children[i]); + children.erase(children.begin() + (i--)); + } else { + children[i]->mem2reg_remove(mem2reg_set, delnodes); + } + } +} + // actually replace memories with registers -void AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block) +bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block) { + bool did_something = false; + if (type == AST_BLOCK) block = this; @@ -2389,6 +2685,8 @@ void AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, children[0]->id2ast = NULL; children[0]->str = id_data; type = AST_ASSIGN_EQ; + + did_something = true; } if (mem2reg_check(mem2reg_set)) @@ -2489,10 +2787,13 @@ void AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, auto children_list = children; for (size_t i = 0; i < children_list.size(); i++) - children_list[i]->mem2reg_as_needed_pass2(mem2reg_set, mod, block); + if (children_list[i]->mem2reg_as_needed_pass2(mem2reg_set, mod, block)) + did_something = true; + + return did_something; } -// calulate memory dimensions +// calculate memory dimensions void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits) { log_assert(type == AST_MEMORY); @@ -2502,7 +2803,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) @@ -2538,8 +2839,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; @@ -2579,7 +2880,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()); @@ -2610,6 +2911,9 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_ASSIGN_EQ) { + if (stmt->children.at(0)->type == AST_IDENTIFIER && stmt->children.at(0)->children.size() != 0 && + stmt->children.at(0)->children.at(0)->type == AST_RANGE) + stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall); stmt->children.at(1)->replace_variables(variables, fcall); while (stmt->simplify(true, false, false, 1, -1, false, true)) { } @@ -2635,7 +2939,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()); @@ -2708,7 +3012,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) for (size_t i = 1; i < stmt->children.size(); i++) { bool found_match = false; - log_assert(stmt->children.at(i)->type == AST_COND); + log_assert(stmt->children.at(i)->type == AST_COND || stmt->children.at(i)->type == AST_CONDX || stmt->children.at(i)->type == AST_CONDZ); if (stmt->children.at(i)->children.front()->type == AST_DEFAULT) { sel_case = stmt->children.at(i)->children.back(); diff --git a/frontends/blif/Makefile.inc b/frontends/blif/Makefile.inc new file mode 100644 index 000000000..9729184eb --- /dev/null +++ b/frontends/blif/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += frontends/blif/blifparse.o + diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc new file mode 100644 index 000000000..1f6d0ee37 --- /dev/null +++ b/frontends/blif/blifparse.cc @@ -0,0 +1,485 @@ +/* + * 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 "blifparse.h" + +YOSYS_NAMESPACE_BEGIN + +static bool read_next_line(char *&buffer, size_t &buffer_size, int &line_count, std::istream &f) +{ + int buffer_len = 0; + buffer[0] = 0; + + while (1) + { + buffer_len += strlen(buffer + buffer_len); + while (buffer_len > 0 && (buffer[buffer_len-1] == ' ' || buffer[buffer_len-1] == '\t' || + buffer[buffer_len-1] == '\r' || buffer[buffer_len-1] == '\n')) + buffer[--buffer_len] = 0; + + if (buffer_size-buffer_len < 4096) { + buffer_size *= 2; + buffer = (char*)realloc(buffer, buffer_size); + } + + if (buffer_len == 0 || buffer[buffer_len-1] == '\\') { + if (buffer_len > 0 && buffer[buffer_len-1] == '\\') + buffer[--buffer_len] = 0; + line_count++; + if (!f.getline(buffer+buffer_len, buffer_size-buffer_len)) + return false; + } else + return true; + } +} + +void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean, bool sop_mode) +{ + RTLIL::Module *module = nullptr; + RTLIL::Const *lutptr = NULL; + RTLIL::Cell *sopcell = NULL; + RTLIL::State lut_default_state = RTLIL::State::Sx; + int blif_maxnum = 0, sopmode = -1; + + 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; + + size_t buffer_size = 4096; + char *buffer = (char*)malloc(buffer_size); + int line_count = 0; + + while (1) + { + if (!read_next_line(buffer, buffer_size, line_count, f)) { + if (module != nullptr) + goto error; + free(buffer); + return; + } + + continue_without_read: + if (buffer[0] == '#') + continue; + + if (buffer[0] == '.') + { + if (lutptr) { + for (auto &bit : lutptr->bits) + if (bit == RTLIL::State::Sx) + bit = lut_default_state; + lutptr = NULL; + lut_default_state = RTLIL::State::Sx; + } + + if (sopcell) { + sopcell = NULL; + sopmode = -1; + } + + char *cmd = strtok(buffer, " \t\r\n"); + + if (!strcmp(cmd, ".model")) { + if (module != nullptr) + goto error; + module = new RTLIL::Module; + module->name = RTLIL::escape_id(strtok(NULL, " \t\r\n")); + obj_attributes = &module->attributes; + obj_parameters = nullptr; + if (design->module(module->name)) + log_error("Duplicate definition of module %s in line %d!\n", log_id(module->name), line_count); + design->add(module); + continue; + } + + if (module == nullptr) + goto error; + + 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; + continue; + } + + if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs")) { + char *p; + while ((p = strtok(NULL, " \t\r\n")) != NULL) { + 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 + wire->port_output = true; + } + obj_attributes = nullptr; + obj_parameters = nullptr; + continue; + } + + if (!strcmp(cmd, ".attr") || !strcmp(cmd, ".param")) { + char *n = strtok(NULL, " \t\r\n"); + char *v = strtok(NULL, "\r\n"); + IdString id_n = RTLIL::escape_id(n); + Const const_v; + if (v[0] == '"') { + std::string str(v+1); + if (str.back() == '"') + str.resize(str.size()-1); + const_v = Const(str); + } else { + int n = strlen(v); + const_v.bits.resize(n); + for (int i = 0; i < n; i++) + const_v.bits[i] = v[n-i-1] != '0' ? State::S1 : State::S0; + } + if (!strcmp(cmd, ".attr")) { + if (obj_attributes == nullptr) + goto error; + (*obj_attributes)[id_n] = const_v; + } else { + if (obj_parameters == nullptr) + goto error; + (*obj_parameters)[id_n] = const_v; + } + continue; + } + + if (!strcmp(cmd, ".latch")) + { + char *d = strtok(NULL, " \t\r\n"); + char *q = strtok(NULL, " \t\r\n"); + char *edge = strtok(NULL, " \t\r\n"); + char *clock = strtok(NULL, " \t\r\n"); + char *init = strtok(NULL, " \t\r\n"); + RTLIL::Cell *cell = nullptr; + + if (clock == nullptr && edge != nullptr) { + init = edge; + edge = nullptr; + } + + if (init != nullptr && (init[0] == '0' || init[0] == '1')) + blif_wire(d)->attributes["\\init"] = Const(init[0] == '1' ? 1 : 0, 1); + + if (clock == nullptr) + goto no_latch_clock; + + if (!strcmp(edge, "re")) + cell = module->addDff(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q)); + else if (!strcmp(edge, "fe")) + cell = module->addDff(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q), false); + else if (!strcmp(edge, "ah")) + cell = module->addDlatch(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q)); + else if (!strcmp(edge, "al")) + cell = module->addDlatch(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", blif_wire(d)); + cell->setPort("\\Q", blif_wire(q)); + } + + obj_attributes = &cell->attributes; + obj_parameters = &cell->parameters; + continue; + } + + if (!strcmp(cmd, ".gate") || !strcmp(cmd, ".subckt")) + { + char *p = strtok(NULL, " \t\r\n"); + if (p == NULL) + goto error; + + IdString celltype = RTLIL::escape_id(p); + RTLIL::Cell *cell = module->addCell(NEW_ID, celltype); + + while ((p = strtok(NULL, " \t\r\n")) != NULL) { + char *q = strchr(p, '='); + if (q == NULL || !q[0]) + goto error; + *(q++) = 0; + cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec()); + } + + obj_attributes = &cell->attributes; + obj_parameters = &cell->parameters; + continue; + } + + obj_attributes = nullptr; + obj_parameters = nullptr; + + if (!strcmp(cmd, ".barbuf")) + { + char *p = strtok(NULL, " \t\r\n"); + if (p == NULL) + goto error; + + char *q = strtok(NULL, " \t\r\n"); + if (q == NULL) + goto error; + + module->connect(blif_wire(q), blif_wire(p)); + continue; + } + + if (!strcmp(cmd, ".names")) + { + char *p; + RTLIL::SigSpec input_sig, output_sig; + 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) + { + RTLIL::State state = RTLIL::State::Sa; + while (1) { + if (!read_next_line(buffer, buffer_size, line_count, f)) + goto error; + for (int i = 0; buffer[i]; i++) { + if (buffer[i] == ' ' || buffer[i] == '\t') + continue; + if (i == 0 && buffer[i] == '.') + goto finished_parsing_constval; + if (buffer[i] == '0') { + if (state == RTLIL::State::S1) + goto error; + state = RTLIL::State::S0; + continue; + } + if (buffer[i] == '1') { + if (state == RTLIL::State::S0) + goto error; + state = RTLIL::State::S1; + continue; + } + 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; + } + + if (sop_mode) + { + sopcell = module->addCell(NEW_ID, "$sop"); + sopcell->parameters["\\WIDTH"] = RTLIL::Const(input_sig.size()); + sopcell->parameters["\\DEPTH"] = 0; + sopcell->parameters["\\TABLE"] = RTLIL::Const(); + sopcell->setPort("\\A", input_sig); + sopcell->setPort("\\Y", output_sig); + sopmode = -1; + } + else + { + RTLIL::Cell *cell = module->addCell(NEW_ID, "$lut"); + cell->parameters["\\WIDTH"] = RTLIL::Const(input_sig.size()); + cell->parameters["\\LUT"] = RTLIL::Const(RTLIL::State::Sx, 1 << input_sig.size()); + cell->setPort("\\A", input_sig); + cell->setPort("\\Y", output_sig); + lutptr = &cell->parameters.at("\\LUT"); + lut_default_state = RTLIL::State::Sx; + } + continue; + } + + goto error; + } + + if (lutptr == NULL && sopcell == NULL) + goto error; + + char *input = strtok(buffer, " \t\r\n"); + char *output = strtok(NULL, " \t\r\n"); + + if (input == NULL || output == NULL || (strcmp(output, "0") && strcmp(output, "1"))) + goto error; + + int input_len = strlen(input); + + if (sopcell) + { + log_assert(sopcell->parameters["\\WIDTH"].as_int() == input_len); + sopcell->parameters["\\DEPTH"] = sopcell->parameters["\\DEPTH"].as_int() + 1; + + for (int i = 0; i < input_len; i++) + switch (input[i]) { + case '0': + sopcell->parameters["\\TABLE"].bits.push_back(State::S1); + sopcell->parameters["\\TABLE"].bits.push_back(State::S0); + break; + case '1': + sopcell->parameters["\\TABLE"].bits.push_back(State::S0); + sopcell->parameters["\\TABLE"].bits.push_back(State::S1); + break; + default: + sopcell->parameters["\\TABLE"].bits.push_back(State::S0); + sopcell->parameters["\\TABLE"].bits.push_back(State::S0); + break; + } + + if (sopmode == -1) { + sopmode = (*output == '1'); + if (!sopmode) { + SigSpec outnet = sopcell->getPort("\\Y"); + SigSpec tempnet = module->addWire(NEW_ID); + module->addNotGate(NEW_ID, tempnet, outnet); + sopcell->setPort("\\Y", tempnet); + } + } else + log_assert(sopmode == (*output == '1')); + } + + if (lutptr) + { + if (input_len > 8) + goto error; + + for (int i = 0; i < (1 << input_len); i++) { + for (int j = 0; j < input_len; j++) { + char c1 = input[j]; + if (c1 != '-') { + char c2 = (i & (1 << j)) != 0 ? '1' : '0'; + if (c1 != c2) + goto try_next_value; + } + } + lutptr->bits.at(i) = !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1; + try_next_value:; + } + + lut_default_state = !strcmp(output, "0") ? RTLIL::State::S1 : RTLIL::State::S0; + } + } + +error: + log_error("Syntax error in line %d!\n", line_count); +} + +struct BlifFrontend : public Frontend { + BlifFrontend() : Frontend("blif", "read BLIF file") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" read_blif [filename]\n"); + log("\n"); + log("Load modules from a BLIF file into the current design.\n"); + log("\n"); + log(" -sop\n"); + log(" Create $sop cells instead of $lut cells\n"); + log("\n"); + } + virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + { + bool sop_mode = false; + + log_header(design, "Executing BLIF frontend.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-sop") { + sop_mode = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + parse_blif(design, *f, "\\DFF", true, sop_mode); + } +} BlifFrontend; + +YOSYS_NAMESPACE_END + diff --git a/passes/abc/blifparse.h b/frontends/blif/blifparse.h index 31f5b2e5c..058087d81 100644 --- a/passes/abc/blifparse.h +++ b/frontends/blif/blifparse.h @@ -2,11 +2,11 @@ * 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 @@ -24,7 +24,7 @@ YOSYS_NAMESPACE_BEGIN -extern RTLIL::Design *abc_parse_blif(FILE *f, std::string dff_name); +extern void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean = false, bool sop_mode = false); YOSYS_NAMESPACE_END diff --git a/frontends/ilang/Makefile.inc b/frontends/ilang/Makefile.inc index c15e2cc47..e2a476c93 100644 --- a/frontends/ilang/Makefile.inc +++ b/frontends/ilang/Makefile.inc @@ -5,13 +5,15 @@ GENFILES += frontends/ilang/ilang_parser.output GENFILES += frontends/ilang/ilang_lexer.cc frontends/ilang/ilang_parser.tab.cc: frontends/ilang/ilang_parser.y - $(P) $(BISON) -d -r all -b frontends/ilang/ilang_parser frontends/ilang/ilang_parser.y + $(Q) mkdir -p $(dir $@) + $(P) $(BISON) -d -r all -b frontends/ilang/ilang_parser $< $(Q) mv frontends/ilang/ilang_parser.tab.c frontends/ilang/ilang_parser.tab.cc frontends/ilang/ilang_parser.tab.h: frontends/ilang/ilang_parser.tab.cc frontends/ilang/ilang_lexer.cc: frontends/ilang/ilang_lexer.l - $(P) flex -o frontends/ilang/ilang_lexer.cc frontends/ilang/ilang_lexer.l + $(Q) mkdir -p $(dir $@) + $(P) flex -o frontends/ilang/ilang_lexer.cc $< OBJS += frontends/ilang/ilang_parser.tab.o frontends/ilang/ilang_lexer.o OBJS += frontends/ilang/ilang_frontend.o diff --git a/frontends/ilang/ilang_frontend.cc b/frontends/ilang/ilang_frontend.cc index 7a4687a3c..ed6789987 100644 --- a/frontends/ilang/ilang_frontend.cc +++ b/frontends/ilang/ilang_frontend.cc @@ -2,11 +2,11 @@ * 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 @@ -47,7 +47,7 @@ struct IlangFrontend : public Frontend { } virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing ILANG frontend.\n"); + log_header(design, "Executing ILANG frontend.\n"); extra_args(f, filename, args, 1); log("Input filename: %s\n", filename.c_str()); diff --git a/frontends/ilang/ilang_frontend.h b/frontends/ilang/ilang_frontend.h index b04d6c512..ad3ffec90 100644 --- a/frontends/ilang/ilang_frontend.h +++ b/frontends/ilang/ilang_frontend.h @@ -2,11 +2,11 @@ * 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 diff --git a/frontends/ilang/ilang_lexer.l b/frontends/ilang/ilang_lexer.l index ace992fbd..415de74eb 100644 --- a/frontends/ilang/ilang_lexer.l +++ b/frontends/ilang/ilang_lexer.l @@ -2,11 +2,11 @@ * 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 @@ -29,7 +29,7 @@ #pragma clang diagnostic ignored "-Wdeprecated-register" #endif -#include "ilang_frontend.h" +#include "frontends/ilang/ilang_frontend.h" #include "ilang_parser.tab.h" USING_YOSYS_NAMESPACE diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y index 4661d5772..cc31c8642 100644 --- a/frontends/ilang/ilang_parser.y +++ b/frontends/ilang/ilang_parser.y @@ -2,11 +2,11 @@ * 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 @@ -24,7 +24,7 @@ %{ #include <list> -#include "ilang_frontend.h" +#include "frontends/ilang/ilang_frontend.h" YOSYS_NAMESPACE_BEGIN namespace ILANG_FRONTEND { std::istream *lexin; @@ -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/liberty/liberty.cc b/frontends/liberty/liberty.cc index 464c5c942..0be58b6da 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -2,11 +2,11 @@ * 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 @@ -40,7 +40,7 @@ static RTLIL::SigSpec parse_func_identifier(RTLIL::Module *module, const char *& if (id_len == 0) log_error("Expected identifier at `%s'.\n", expr); - + if (id_len == 1 && (*expr == '0' || *expr == '1')) return *(expr++) == '0' ? RTLIL::State::S0 : RTLIL::State::S1; @@ -437,7 +437,7 @@ struct LibertyFrontend : public Frontend { bool flag_ignore_miss_dir = false; std::vector<std::string> attributes; - log_header("Executing Liberty frontend.\n"); + log_header(design, "Executing Liberty frontend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/frontends/verific/Makefile.inc b/frontends/verific/Makefile.inc index 13f242c4b..68ef9aed1 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 9bb6e3203..d6952820e 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 @@ -21,7 +19,7 @@ VERIFIC_DIR = /usr/local/src/verific_lib_eval 2.) Install the necessary multilib packages Hint: On debian/ubuntu the multilib packages have names such as -libreadline-dev:amd64 or lib32readline6-dev, depending on the +libreadline-dev:i386 or lib32readline6-dev, depending on the exact version of debian/ubuntu you are working with. diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 79abcf245..7dd36a747 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -2,11 +2,11 @@ * 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 @@ -65,7 +65,7 @@ static void msg_func(msg_type_t msg_type, const char *message_id, linefile_type log("\n"); } -static void import_attributes(std::map<RTLIL::IdString, RTLIL::Const> &attributes, DesignObj *obj) +static void import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, DesignObj *obj) { MapIter mi; Att *attr; @@ -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; @@ -692,7 +737,8 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist* cell->parameters["\\TRANSPARENT"] = false; cell->parameters["\\ABITS"] = GetSize(addr); cell->parameters["\\WIDTH"] = GetSize(data); - cell->setPort("\\CLK", RTLIL::State::S0); + cell->setPort("\\CLK", RTLIL::State::Sx); + cell->setPort("\\EN", RTLIL::State::Sx); cell->setPort("\\ADDR", addr); cell->setPort("\\DATA", data); continue; @@ -737,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(); @@ -751,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); } } } @@ -789,7 +840,7 @@ struct VerificPass : public Pass { log("\n"); log(" verific -import [-gates] {-all | <top-module>..}\n"); log("\n"); - log("Elaborate the design for the sepcified top modules, import to Yosys and\n"); + log("Elaborate the design for the specified top modules, import to Yosys and\n"); log("reset the internal state of Verific. A gate-level netlist is created\n"); log("when called with -gates.\n"); log("\n"); @@ -799,7 +850,7 @@ struct VerificPass : public Pass { #ifdef YOSYS_ENABLE_VERIFIC virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing VERIFIC (loading Verilog and VHDL designs using Verific).\n"); + log_header(design, "Executing VERIFIC (loading Verilog and VHDL designs using Verific).\n"); Message::SetConsoleOutput(0); Message::RegisterCallBackMsg(msg_func); @@ -840,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()); @@ -917,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()); @@ -947,6 +1000,6 @@ struct VerificPass : public Pass { } #endif } VerificPass; - + YOSYS_NAMESPACE_END diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc index 92cbd0b87..a06c1d5ab 100644 --- a/frontends/verilog/Makefile.inc +++ b/frontends/verilog/Makefile.inc @@ -5,13 +5,15 @@ GENFILES += frontends/verilog/verilog_parser.output GENFILES += frontends/verilog/verilog_lexer.cc frontends/verilog/verilog_parser.tab.cc: frontends/verilog/verilog_parser.y - $(P) $(BISON) -d -r all -b frontends/verilog/verilog_parser frontends/verilog/verilog_parser.y + $(Q) mkdir -p $(dir $@) + $(P) $(BISON) -d -r all -b frontends/verilog/verilog_parser $< $(Q) mv frontends/verilog/verilog_parser.tab.c frontends/verilog/verilog_parser.tab.cc frontends/verilog/verilog_parser.tab.h: frontends/verilog/verilog_parser.tab.cc frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l - $(P) flex -o frontends/verilog/verilog_lexer.cc frontends/verilog/verilog_lexer.l + $(Q) mkdir -p $(dir $@) + $(P) flex -o frontends/verilog/verilog_lexer.cc $< OBJS += frontends/verilog/verilog_parser.tab.o OBJS += frontends/verilog/verilog_lexer.o diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc index 735bc5f99..4a58357bf 100644 --- a/frontends/verilog/const2ast.cc +++ b/frontends/verilog/const2ast.cc @@ -2,11 +2,11 @@ * 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 @@ -48,7 +48,9 @@ static int my_decimal_div_by_two(std::vector<uint8_t> &digits) { int carry = 0; for (size_t i = 0; i < digits.size(); i++) { - log_assert(digits[i] < 10); + if (digits[i] >= 10) + log_error("Invalid use of [a-fxz?] in decimal constant at %s:%d.\n", + current_filename.c_str(), get_line_num()); digits[i] += carry * 10; carry = digits[i] % 2; digits[i] /= 2; @@ -91,54 +93,67 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le str++; } + if (base == 10 && GetSize(digits) == 1 && digits.front() >= 0xf0) + base = 2; + + data.clear(); + if (base == 10) { - data.clear(); - if (len_in_bits < 0) { - while (!digits.empty()) - data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0); - while (data.size() < 32) - data.push_back(RTLIL::S0); - } else { - for (int i = 0; i < len_in_bits; i++) - data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0); + while (!digits.empty()) + data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0); + } else { + int bits_per_digit = my_ilog2(base-1); + for (auto it = digits.rbegin(), e = digits.rend(); it != e; it++) { + if (*it > (base-1) && *it < 0xf0) + log_error("Digit larger than %d used in in base-%d constant at %s:%d.\n", + base-1, base, current_filename.c_str(), get_line_num()); + for (int i = 0; i < bits_per_digit; i++) { + int bitmask = 1 << i; + if (*it == 0xf0) + data.push_back(case_type == 'x' ? RTLIL::Sa : RTLIL::Sx); + else if (*it == 0xf1) + data.push_back(case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz); + else if (*it == 0xf2) + data.push_back(RTLIL::Sa); + else + data.push_back((*it & bitmask) ? RTLIL::S1 : RTLIL::S0); + } } - return; } - int bits_per_digit = my_ilog2(base-1); - if (len_in_bits < 0) - len_in_bits = std::max<int>(digits.size() * bits_per_digit, 32); + int len = GetSize(data); + RTLIL::State msb = data.empty() ? RTLIL::S0 : data.back(); - data.clear(); - data.resize(len_in_bits); - - for (int i = 0; i < len_in_bits; i++) { - int bitmask = 1 << (i % bits_per_digit); - int digitidx = digits.size() - (i / bits_per_digit) - 1; - if (digitidx < 0) { - if (i > 0 && (data[i-1] == RTLIL::Sz || data[i-1] == RTLIL::Sx || data[i-1] == RTLIL::Sa)) - data[i] = data[i-1]; - else - data[i] = RTLIL::S0; - } else if (digits[digitidx] == 0xf0) - data[i] = case_type == 'x' ? RTLIL::Sa : RTLIL::Sx; - else if (digits[digitidx] == 0xf1) - data[i] = case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz; - else if (digits[digitidx] == 0xf2) - data[i] = RTLIL::Sa; - else - data[i] = (digits[digitidx] & bitmask) ? RTLIL::S1 : RTLIL::S0; + if (len_in_bits < 0) { + if (len < 32) + data.resize(32, msb == RTLIL::S0 || msb == RTLIL::S1 ? RTLIL::S0 : msb); + return; + } + + for (len = len - 1; len >= 0; len--) + if (data[len] == RTLIL::S1) + break; + if (msb == RTLIL::S0 || msb == RTLIL::S1) { + len += 1; + data.resize(len_in_bits, RTLIL::S0); + } else { + len += 2; + data.resize(len_in_bits, msb); } + + if (len > len_in_bits) + log_warning("Literal has a width of %d bit, but value requires %d bit. (%s:%d)\n", + len_in_bits, len, current_filename.c_str(), get_line_num()); } -// convert the verilog code for a constant to an AST node +// convert the Verilog code for a constant to an AST node AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn_z) { if (warn_z) { AstNode *ret = const2ast(code, case_type); if (std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end()) - log_warning("Yosys does not support tri-state logic at the moment. (%s:%d)\n", - current_filename.c_str(), frontend_verilog_yyget_lineno()); + log_warning("Yosys has only limited support for tri-state logic at the moment. (%s:%d)\n", + current_filename.c_str(), get_line_num()); return ret; } @@ -215,8 +230,6 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn if (len_in_bits < 0) { if (is_signed && data.back() == RTLIL::S1) data.push_back(RTLIL::S0); - while (data.size() < 32) - data.push_back(RTLIL::S0); } return AstNode::mkconst_bits(data, is_signed); } diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index 4e5d16599..997920b89 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -2,11 +2,11 @@ * 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 @@ -39,6 +39,7 @@ #include <string.h> YOSYS_NAMESPACE_BEGIN +using namespace VERILOG_FRONTEND; static std::list<std::string> output_code; static std::list<std::string> input_buffer; @@ -109,7 +110,7 @@ static std::string next_token(bool pass_newline = false) } return token; } - + if (ch == ' ' || ch == '\t') { while ((ch = next_char()) != 0) { @@ -201,7 +202,7 @@ static void input_file(std::istream &f, std::string filename) insert_input(""); auto it = input_buffer.begin(); - input_buffer.insert(it, "`file_push " + filename + "\n"); + input_buffer.insert(it, "`file_push \"" + filename + "\"\n"); while ((rc = readsome(f, buffer, sizeof(buffer)-1)) > 0) { buffer[rc] = 0; input_buffer.insert(it, buffer); @@ -222,7 +223,7 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons input_file(f, filename); defines_map["YOSYS"] = "1"; - defines_map["SYNTHESIS"] = "1"; + defines_map[formal_mode ? "FORMAL" : "SYNTHESIS"] = "1"; while (!input_buffer.empty()) { diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 23d35f682..576f068b3 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -2,11 +2,11 @@ * 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 @@ -39,21 +39,33 @@ using namespace VERILOG_FRONTEND; static std::vector<std::string> verilog_defaults; static std::list<std::vector<std::string>> verilog_defaults_stack; +static void error_on_dpi_function(AST::AstNode *node) +{ + if (node->type == AST::AST_DPI_FUNCTION) + log_error("Found DPI function %s at %s:%d.\n", node->str.c_str(), node->filename.c_str(), node->linenum); + for (auto child : node->children) + error_on_dpi_function(child); +} + struct VerilogFrontend : public Frontend { - VerilogFrontend() : Frontend("verilog", "read modules from verilog file") { } + VerilogFrontend() : Frontend("verilog", "read modules from Verilog file") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" read_verilog [options] [filename]\n"); log("\n"); - log("Load modules from a verilog file to the current design. A large subset of\n"); + log("Load modules from a Verilog file to the current design. A large subset of\n"); log("Verilog-2005 is supported.\n"); log("\n"); log(" -sv\n"); log(" enable support for SystemVerilog features. (only a small subset\n"); log(" of SystemVerilog is supported)\n"); log("\n"); + log(" -formal\n"); + log(" enable support for assert() and assume() from SystemVerilog\n"); + log(" replace the implicit -D SYNTHESIS with -D FORMAL\n"); + log("\n"); log(" -dump_ast1\n"); log(" dump abstract syntax tree (before simplification)\n"); log("\n"); @@ -61,7 +73,7 @@ struct VerilogFrontend : public Frontend { log(" dump abstract syntax tree (after simplification)\n"); log("\n"); log(" -dump_vlog\n"); - log(" dump ast as verilog code (after simplification)\n"); + log(" dump ast as Verilog code (after simplification)\n"); log("\n"); log(" -yydebug\n"); log(" enable parser debug output\n"); @@ -83,19 +95,31 @@ struct VerilogFrontend : public Frontend { log(" this can also be achieved by setting the 'nomem2reg'\n"); log(" attribute on the respective module or register.\n"); log("\n"); + log(" This is potentially dangerous. Usually the front-end has good\n"); + log(" reasons for converting an array to a list of registers.\n"); + log(" Prohibiting this step will likely result in incorrect synthesis\n"); + log(" results.\n"); + log("\n"); log(" -mem2reg\n"); log(" always convert memories to registers. this can also be\n"); log(" achieved by setting the 'mem2reg' attribute on the respective\n"); log(" module or register.\n"); log("\n"); + log(" -nomeminit\n"); + log(" do not infer $meminit cells and instead convert initialized\n"); + log(" memories to registers directly in the front-end.\n"); + log("\n"); log(" -ppdump\n"); - log(" dump verilog code after pre-processor\n"); + log(" dump Verilog code after pre-processor\n"); log("\n"); log(" -nopp\n"); log(" do not run the pre-processor\n"); log("\n"); + log(" -nodpi\n"); + log(" disable DPI-C support\n"); + log("\n"); log(" -lib\n"); - log(" only create empty blackbox modules\n"); + log(" only create empty blackbox modules. This implies -DBLACKBOX.\n"); log("\n"); log(" -noopt\n"); log(" don't perform basic optimizations (such as const folding) in the\n"); @@ -113,6 +137,9 @@ struct VerilogFrontend : public Frontend { log(" to a later 'hierarchy' command. Useful in cases where the default\n"); log(" parameters of modules yield invalid or not synthesizable code.\n"); log("\n"); + log(" -noautowire\n"); + log(" make the default of `default_nettype be \"none\" instead of \"wire\".\n"); + log("\n"); log(" -setattr <attribute_name>\n"); log(" set the specified attribute (to the value 1) on all loaded modules\n"); log("\n"); @@ -129,9 +156,12 @@ struct VerilogFrontend : public Frontend { log("\n"); log("Note that the Verilog frontend does a pretty good job of processing valid\n"); log("verilog input, but has not very good error reporting. It generally is\n"); - log("recommended to use a simulator (for example icarus verilog) for checking\n"); + log("recommended to use a simulator (for example Icarus Verilog) for checking\n"); log("the syntax of the code, rather than to rely on read_verilog for that.\n"); log("\n"); + log("See the Yosys README file for a list of non-standard Verilog features\n"); + log("supported by the Yosys Verilog front-end.\n"); + log("\n"); } virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) { @@ -139,10 +169,12 @@ struct VerilogFrontend : public Frontend { bool flag_dump_ast2 = false; bool flag_dump_vlog = false; bool flag_nolatches = false; + bool flag_nomeminit = false; bool flag_nomem2reg = false; bool flag_mem2reg = false; bool flag_ppdump = false; bool flag_nopp = false; + bool flag_nodpi = false; bool flag_lib = false; bool flag_noopt = false; bool flag_icells = false; @@ -154,8 +186,10 @@ struct VerilogFrontend : public Frontend { frontend_verilog_yydebug = false; sv_mode = false; + formal_mode = false; + default_nettype_wire = true; - log_header("Executing Verilog-2005 frontend.\n"); + log_header(design, "Executing Verilog-2005 frontend.\n"); args.insert(args.begin()+1, verilog_defaults.begin(), verilog_defaults.end()); @@ -166,6 +200,10 @@ struct VerilogFrontend : public Frontend { sv_mode = true; continue; } + if (arg == "-formal") { + formal_mode = true; + continue; + } if (arg == "-dump_ast1") { flag_dump_ast1 = true; continue; @@ -186,6 +224,10 @@ struct VerilogFrontend : public Frontend { flag_nolatches = true; continue; } + if (arg == "-nomeminit") { + flag_nomeminit = true; + continue; + } if (arg == "-nomem2reg") { flag_nomem2reg = true; continue; @@ -202,8 +244,13 @@ struct VerilogFrontend : public Frontend { flag_nopp = true; continue; } + if (arg == "-nodpi") { + flag_nodpi = true; + continue; + } if (arg == "-lib") { flag_lib = true; + defines_map["BLACKBOX"] = string(); continue; } if (arg == "-noopt") { @@ -222,6 +269,10 @@ struct VerilogFrontend : public Frontend { flag_defer = true; continue; } + if (arg == "-noautowire") { + default_nettype_wire = false; + continue; + } if (arg == "-setattr" && argidx+1 < args.size()) { attributes.push_back(RTLIL::escape_id(args[++argidx])); continue; @@ -257,14 +308,14 @@ struct VerilogFrontend : public Frontend { } extra_args(f, filename, args, argidx); - log("Parsing %s input from `%s' to AST representation.\n", sv_mode ? "SystemVerilog" : "Verilog", filename.c_str()); + log("Parsing %s%s input from `%s' to AST representation.\n", + formal_mode ? "formal " : "", sv_mode ? "SystemVerilog" : "Verilog", filename.c_str()); AST::current_filename = filename; AST::set_line_num = &frontend_verilog_yyset_lineno; AST::get_line_num = &frontend_verilog_yyget_lineno; current_ast = new AST::AstNode(AST::AST_DESIGN); - default_nettype_wire = true; lexin = f; std::string code_after_preproc; @@ -288,7 +339,10 @@ struct VerilogFrontend : public Frontend { child->attributes[attr] = AST::AstNode::mkconst_int(1, false); } - AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_ignore_redef, flag_defer, default_nettype_wire); + if (flag_nodpi) + error_on_dpi_function(current_ast); + + AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_ignore_redef, flag_defer, default_nettype_wire); if (!flag_nopp) delete lexin; @@ -308,16 +362,16 @@ struct VerilogDefaults : public Pass { log("\n"); log(" verilog_defaults -add [options]\n"); log("\n"); - log("Add the sepcified options to the list of default options to read_verilog.\n"); + log("Add the specified options to the list of default options to read_verilog.\n"); log("\n"); log("\n"); - log(" verilog_defaults -clear"); + log(" verilog_defaults -clear\n"); log("\n"); - log("Clear the list of verilog default options.\n"); + log("Clear the list of Verilog default options.\n"); log("\n"); log("\n"); - log(" verilog_defaults -push"); - log(" verilog_defaults -pop"); + log(" verilog_defaults -push\n"); + log(" verilog_defaults -pop\n"); log("\n"); log("Push or pop the list of default options to a stack. Note that -push does\n"); log("not imply -clear.\n"); diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h index e277f3e3c..fb98f4afb 100644 --- a/frontends/verilog/verilog_frontend.h +++ b/frontends/verilog/verilog_frontend.h @@ -2,11 +2,11 @@ * 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 @@ -51,6 +51,9 @@ namespace VERILOG_FRONTEND // running in SystemVerilog mode extern bool sv_mode; + // running in -formal mode + extern bool formal_mode; + // lexer input stream extern std::istream *lexin; } diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index cb8fafcb2..107a2dfdd 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -2,11 +2,11 @@ * 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 @@ -40,7 +40,7 @@ #endif #include "kernel/log.h" -#include "verilog_frontend.h" +#include "frontends/verilog/verilog_frontend.h" #include "frontends/ast/ast.h" #include "verilog_parser.tab.h" @@ -85,6 +85,10 @@ YOSYS_NAMESPACE_END fn_stack.push_back(current_filename); ln_stack.push_back(frontend_verilog_yyget_lineno()); current_filename = yytext+11; + if (!current_filename.empty() && current_filename.front() == '"') + current_filename = current_filename.substr(1); + if (!current_filename.empty() && current_filename.back() == '"') + current_filename = current_filename.substr(0, current_filename.size()-1); frontend_verilog_yyset_lineno(0); } @@ -112,6 +116,9 @@ YOSYS_NAMESPACE_END "`timescale"[ \t]+[^ \t\r\n/]+[ \t]*"/"[ \t]*[^ \t\r\n]* /* ignore timescale directive */ +"`celldefine"[^\n]* /* ignore `celldefine */ +"`endcelldefine"[^\n]* /* ignore `endcelldefine */ + "`default_nettype"[ \t]+[^ \t\r\n/]+ { char *p = yytext; while (*p != 0 && *p != ' ' && *p != '\t') p++; @@ -134,6 +141,8 @@ YOSYS_NAMESPACE_END "endfunction" { return TOK_ENDFUNCTION; } "task" { return TOK_TASK; } "endtask" { return TOK_ENDTASK; } +"package" { SV_KEYWORD(TOK_PACKAGE); } +"endpackage" { SV_KEYWORD(TOK_ENDPACKAGE); } "parameter" { return TOK_PARAMETER; } "localparam" { return TOK_LOCALPARAM; } "defparam" { return TOK_DEFPARAM; } @@ -162,8 +171,9 @@ YOSYS_NAMESPACE_END "always_ff" { SV_KEYWORD(TOK_ALWAYS); } "always_latch" { SV_KEYWORD(TOK_ALWAYS); } -"assert" { SV_KEYWORD(TOK_ASSERT); } -"property" { SV_KEYWORD(TOK_PROPERTY); } +"assert" { if (formal_mode) return TOK_ASSERT; SV_KEYWORD(TOK_ASSERT); } +"assume" { if (formal_mode) return TOK_ASSUME; SV_KEYWORD(TOK_ASSUME); } +"property" { if (formal_mode) return TOK_PROPERTY; SV_KEYWORD(TOK_PROPERTY); } "logic" { SV_KEYWORD(TOK_REG); } "bit" { SV_KEYWORD(TOK_REG); } @@ -240,7 +250,7 @@ and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1 { supply0 { return TOK_SUPPLY0; } supply1 { return TOK_SUPPLY1; } -"$"(display|strobe|monitor|time|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) { +"$"(display|write|strobe|monitor|time|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) { frontend_verilog_yylval.string = new std::string(yytext); return TOK_ID; } @@ -273,7 +283,7 @@ supply1 { return TOK_SUPPLY1; } static bool printed_warning = false; if (!printed_warning) { log_warning("Found one of those horrible `(synopsys|synthesis) full_case' comments.\n" - "Yosys does support them but it is recommended to use verilog `full_case' attributes instead!\n"); + "Yosys does support them but it is recommended to use Verilog `full_case' attributes instead!\n"); printed_warning = true; } return TOK_SYNOPSYS_FULL_CASE; @@ -282,7 +292,7 @@ supply1 { return TOK_SUPPLY1; } static bool printed_warning = false; if (!printed_warning) { log_warning("Found one of those horrible `(synopsys|synthesis) parallel_case' comments.\n" - "Yosys does support them but it is recommended to use verilog `parallel_case' attributes instead!\n"); + "Yosys does support them but it is recommended to use Verilog `parallel_case' attributes instead!\n"); printed_warning = true; } return TOK_SYNOPSYS_PARALLEL_CASE; @@ -343,6 +353,8 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { "<<<" { return OP_SSHL; } ">>>" { return OP_SSHR; } +"::" { SV_KEYWORD(TOK_PACKAGESEP); } + "+:" { return TOK_POS_INDEXED; } "-:" { return TOK_NEG_INDEXED; } diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 621b6cc18..e7c3578c7 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -2,11 +2,11 @@ * 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 @@ -36,7 +36,7 @@ %{ #include <list> #include <string.h> -#include "verilog_frontend.h" +#include "frontends/verilog/verilog_frontend.h" #include "kernel/log.h" USING_YOSYS_NAMESPACE @@ -57,7 +57,7 @@ namespace VERILOG_FRONTEND { std::vector<char> case_type_stack; bool do_not_require_port_stubs; bool default_nettype_wire; - bool sv_mode; + bool sv_mode, formal_mode; std::istream *lexin; } YOSYS_NAMESPACE_END @@ -102,6 +102,7 @@ static void free_attr(std::map<std::string, AstNode*> *al) %token <string> TOK_STRING TOK_ID TOK_CONST TOK_REALVAL TOK_PRIMITIVE %token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END %token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM +%token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP %token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG %token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL %token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT @@ -111,7 +112,7 @@ static void free_attr(std::map<std::string, AstNode*> *al) %token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR TOK_REAL %token TOK_SYNOPSYS_FULL_CASE TOK_SYNOPSYS_PARALLEL_CASE %token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED -%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_ASSERT TOK_PROPERTY +%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_ASSERT TOK_ASSUME TOK_PROPERTY %type <ast> range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int %type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list @@ -139,6 +140,7 @@ static void free_attr(std::map<std::string, AstNode*> *al) %% input: { + ast_stack.clear(); ast_stack.push_back(current_ast); } design { ast_stack.pop_back(); @@ -152,6 +154,9 @@ design: module design | defattr design | task_func_decl design | + param_decl design | + localparam_decl design | + package design | /* empty */; attr: @@ -209,6 +214,14 @@ hierarchical_id: TOK_ID { $$ = $1; } | + hierarchical_id TOK_PACKAGESEP TOK_ID { + if ($3->substr(0, 1) == "\\") + *$1 += "::" + $3->substr(1); + else + *$1 += "::" + *$3; + delete $3; + $$ = $1; + } | hierarchical_id '.' TOK_ID { if ($3->substr(0, 1) == "\\") *$1 += "." + $3->substr(1); @@ -243,11 +256,10 @@ module_para_opt: '#' '(' { astbuf1 = nullptr; } module_para_list { if (astbuf1) delete astbuf1; } ')' | /* empty */; module_para_list: - single_module_para | - single_module_para ',' module_para_list | - /* empty */; + single_module_para | module_para_list ',' single_module_para; single_module_para: + /* empty */ | TOK_PARAMETER { if (astbuf1) delete astbuf1; astbuf1 = new AstNode(AST_PARAMETER); @@ -299,7 +311,7 @@ module_arg: node->children.push_back($3); if (!node->is_input && !node->is_output) frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $4->c_str()); - if (node->is_reg && node->is_input && !node->is_output) + if (node->is_reg && node->is_input && !node->is_output && !sv_mode) frontend_verilog_yyerror("Input port `%s' is declared as register.", $4->c_str()); ast_stack.back()->children.push_back(node); append_attr(node, $1); @@ -309,10 +321,36 @@ module_arg: do_not_require_port_stubs = true; }; +package: + attr TOK_PACKAGE TOK_ID { + AstNode *mod = new AstNode(AST_PACKAGE); + ast_stack.back()->children.push_back(mod); + ast_stack.push_back(mod); + current_ast_mod = mod; + mod->str = *$3; + append_attr(mod, $1); + } ';' package_body TOK_ENDPACKAGE { + ast_stack.pop_back(); + current_ast_mod = NULL; + }; + +package_body: + package_body package_body_stmt |; + +package_body_stmt: + localparam_decl; + +non_opt_delay: + '#' '(' expr ')' { delete $3; } | + '#' '(' expr ':' expr ':' expr ')' { delete $3; delete $5; delete $7; }; + +delay: + non_opt_delay | /* empty */; + wire_type: { astbuf3 = new AstNode(AST_WIRE); - } wire_type_token_list { + } wire_type_token_list delay { $$ = astbuf3; }; @@ -700,6 +738,8 @@ wire_name_and_opt_assign: wire_name: TOK_ID range_or_multirange { + if (astbuf1 == nullptr) + frontend_verilog_yyerror("Syntax error."); AstNode *node = astbuf1->clone(); node->str = *$1; append_attr_clone(node, albuf); @@ -724,7 +764,7 @@ wire_name: if (port_stubs.count(*$1) != 0) { if (!node->is_input && !node->is_output) frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $1->c_str()); - if (node->is_reg && node->is_input && !node->is_output) + if (node->is_reg && node->is_input && !node->is_output && !sv_mode) frontend_verilog_yyerror("Input port `%s' is declared as register.", $1->c_str()); node->port_id = port_stubs[*$1]; port_stubs.erase(*$1); @@ -741,13 +781,13 @@ wire_name: }; assign_stmt: - TOK_ASSIGN assign_expr_list ';'; + TOK_ASSIGN delay assign_expr_list ';'; 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)); }; @@ -761,7 +801,7 @@ cell_stmt: } cell_parameter_list_opt cell_list ';' { delete astbuf1; } | - attr tok_prim_wrapper { + attr tok_prim_wrapper delay { astbuf1 = new AstNode(AST_PRIMITIVE); astbuf1->str = *$2; append_attr(astbuf1, $1); @@ -813,10 +853,10 @@ cell_parameter_list_opt: '#' '(' cell_parameter_list ')' | /* empty */; cell_parameter_list: - /* empty */ | cell_parameter | - cell_parameter ',' cell_parameter_list; + cell_parameter | cell_parameter_list ',' cell_parameter; cell_parameter: + /* empty */ | expr { AstNode *node = new AstNode(AST_PARASET); astbuf1->children.push_back(node); @@ -831,14 +871,10 @@ cell_parameter: }; cell_port_list: - /* empty */ | cell_port | - cell_port ',' cell_port_list | - /* empty */ ',' { - AstNode *node = new AstNode(AST_ARGUMENT); - astbuf2->children.push_back(node); - } cell_port_list; + cell_port | cell_port_list ',' cell_port; cell_port: + /* empty */ | expr { AstNode *node = new AstNode(AST_ARGUMENT); astbuf2->children.push_back(node); @@ -926,27 +962,34 @@ opt_label: assert: TOK_ASSERT '(' expr ')' ';' { ast_stack.back()->children.push_back(new AstNode(AST_ASSERT, $3)); + } | + TOK_ASSUME '(' expr ')' ';' { + ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $3)); }; assert_property: TOK_ASSERT TOK_PROPERTY '(' expr ')' ';' { ast_stack.back()->children.push_back(new AstNode(AST_ASSERT, $4)); + } | + TOK_ASSUME TOK_PROPERTY '(' expr ')' ';' { + ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $4)); }; simple_behavioral_stmt: - lvalue '=' expr { - AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $3); + lvalue '=' delay expr { + AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $4); ast_stack.back()->children.push_back(node); } | - lvalue OP_LE expr { - AstNode *node = new AstNode(AST_ASSIGN_LE, $1, $3); + lvalue OP_LE delay expr { + AstNode *node = new AstNode(AST_ASSIGN_LE, $1, $4); ast_stack.back()->children.push_back(node); }; // this production creates the obligatory if-else shift/reduce conflict behavioral_stmt: - defattr | assert | wire_decl | - simple_behavioral_stmt ';' | + defattr | assert | wire_decl | param_decl | localparam_decl | + non_opt_delay behavioral_stmt | + simple_behavioral_stmt ';' | ';' | hierarchical_id attr { AstNode *node = new AstNode(AST_TCALL); node->str = *$1; @@ -1039,13 +1082,13 @@ behavioral_stmt: }; case_type: - TOK_CASE { + TOK_CASE { case_type_stack.push_back(0); } | - TOK_CASEX { + TOK_CASEX { case_type_stack.push_back('x'); } | - TOK_CASEZ { + TOK_CASEZ { case_type_stack.push_back('z'); }; @@ -1060,10 +1103,6 @@ opt_synopsys_attr: } | /* empty */; -behavioral_stmt_opt: - behavioral_stmt | - ';' ; - behavioral_stmt_list: behavioral_stmt_list behavioral_stmt | /* empty */; @@ -1084,7 +1123,9 @@ case_body: case_item: { - AstNode *node = new AstNode(AST_COND); + AstNode *node = new AstNode( + case_type_stack.size() && case_type_stack.back() == 'x' ? AST_CONDX : + case_type_stack.size() && case_type_stack.back() == 'z' ? AST_CONDZ : AST_COND); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); } case_select { @@ -1092,7 +1133,7 @@ case_item: ast_stack.back()->children.push_back(block); ast_stack.push_back(block); case_type_stack.push_back(0); - } behavioral_stmt_opt { + } behavioral_stmt { case_type_stack.pop_back(); ast_stack.pop_back(); ast_stack.pop_back(); @@ -1104,7 +1145,9 @@ gen_case_body: gen_case_item: { - AstNode *node = new AstNode(AST_COND); + AstNode *node = new AstNode( + case_type_stack.size() && case_type_stack.back() == 'x' ? AST_CONDX : + case_type_stack.size() && case_type_stack.back() == 'z' ? AST_CONDZ : AST_COND); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); } case_select { @@ -1330,6 +1373,11 @@ basic_expr: '(' expr ')' { $$ = $2; } | + '(' expr ':' expr ':' expr ')' { + delete $2; + $$ = $4; + delete $6; + } | '{' concat_list '}' { $$ = $2; } | diff --git a/frontends/vhdl2verilog/vhdl2verilog.cc b/frontends/vhdl2verilog/vhdl2verilog.cc index 82ff7b502..6f9c0e3f5 100644 --- a/frontends/vhdl2verilog/vhdl2verilog.cc +++ b/frontends/vhdl2verilog/vhdl2verilog.cc @@ -2,11 +2,11 @@ * 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 @@ -74,7 +74,7 @@ struct Vhdl2verilogPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing VHDL2VERILOG (importing VHDL designs using vhdl2verilog).\n"); + log_header(design, "Executing VHDL2VERILOG (importing VHDL designs using vhdl2verilog).\n"); log_push(); std::string out_file, top_entity; @@ -173,11 +173,11 @@ struct Vhdl2verilogPass : public Pass { Frontend::frontend_call(design, &ff, stringf("%s/vhdl2verilog_output.v", tempdir_name.c_str()), "verilog"); } - log_header("Removing temp directory `%s':\n", tempdir_name.c_str()); + log_header(design, "Removing temp directory `%s':\n", tempdir_name.c_str()); remove_directory(tempdir_name); log_pop(); } } Vhdl2verilogPass; - + YOSYS_NAMESPACE_END diff --git a/kernel/bitpattern.h b/kernel/bitpattern.h index 00bbc3bfb..894a95ed1 100644 --- a/kernel/bitpattern.h +++ b/kernel/bitpattern.h @@ -2,11 +2,11 @@ * 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 @@ -30,7 +30,7 @@ struct BitPatternPool int width; struct bits_t { std::vector<RTLIL::State> bitdata; - unsigned int cached_hash; + mutable unsigned int cached_hash; bits_t(int width = 0) : bitdata(width), cached_hash(0) { } RTLIL::State &operator[](int index) { return bitdata[index]; @@ -45,7 +45,7 @@ struct BitPatternPool } unsigned int hash() const { if (!cached_hash) - ((bits_t*)this)->cached_hash = hash_ops<std::vector<RTLIL::State>>::hash(bitdata); + cached_hash = hash_ops<std::vector<RTLIL::State>>::hash(bitdata); return cached_hash; } }; @@ -154,7 +154,7 @@ struct BitPatternPool { return database.empty(); } -}; +}; YOSYS_NAMESPACE_END diff --git a/kernel/calc.cc b/kernel/calc.cc index aa3e8b919..a24fa2abf 100644 --- a/kernel/calc.cc +++ b/kernel/calc.cc @@ -2,11 +2,11 @@ * 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 @@ -41,21 +41,28 @@ static void extend_u0(RTLIL::Const &arg, int width, bool is_signed) static BigInteger const2big(const RTLIL::Const &val, bool as_signed, int &undef_bit_pos) { - BigInteger result = 0, this_bit = 1; - for (size_t i = 0; i < val.bits.size(); i++) { - if (val.bits[i] == RTLIL::State::S1) { - if (as_signed && i+1 == val.bits.size()) - result -= this_bit; - else - result += this_bit; - } - else if (val.bits[i] != RTLIL::State::S0) { - if (undef_bit_pos < 0) - undef_bit_pos = i; - } - this_bit *= 2; + BigUnsigned mag; + + BigInteger::Sign sign = BigInteger::positive; + State inv_sign_bit = RTLIL::State::S1; + size_t num_bits = val.bits.size(); + + if (as_signed && num_bits && val.bits[num_bits-1] == RTLIL::State::S1) { + inv_sign_bit = RTLIL::State::S0; + sign = BigInteger::negative; + num_bits--; } - return result; + + for (size_t i = 0; i < num_bits; i++) + if (val.bits[i] == RTLIL::State::S0 || val.bits[i] == RTLIL::State::S1) + mag.setBit(i, val.bits[i] == inv_sign_bit); + else if (undef_bit_pos < 0) + undef_bit_pos = i; + + if (sign == BigInteger::negative) + mag += 1; + + return BigInteger(mag, sign); } static RTLIL::Const big2const(const BigInteger &val, int result_len, int undef_bit_pos) @@ -147,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); @@ -303,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); } @@ -382,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); @@ -416,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); @@ -465,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) @@ -492,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) @@ -505,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) @@ -556,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 new file mode 100644 index 000000000..41f81355d --- /dev/null +++ b/kernel/cellaigs.cc @@ -0,0 +1,481 @@ +/* + * 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/cellaigs.h" + +YOSYS_NAMESPACE_BEGIN + +AigNode::AigNode() +{ + portbit = -1; + inverter = false; + left_parent = -1; + right_parent = -1; +} + +bool AigNode::operator==(const AigNode &other) const +{ + if (portname != other.portname) return false; + if (portbit != other.portbit) return false; + if (inverter != other.inverter) return false; + if (left_parent != other.left_parent) return false; + if (right_parent != other.right_parent) return false; + return true; +} + +unsigned int AigNode::hash() const +{ + unsigned int h = mkhash_init; + h = mkhash(portname.hash(), portbit); + h = mkhash(h, inverter); + h = mkhash(h, left_parent); + h = mkhash(h, right_parent); + return h; +} + +bool Aig::operator==(const Aig &other) const +{ + return name == other.name; +} + +unsigned int Aig::hash() const +{ + return hash_ops<std::string>::hash(name); +} + +struct AigMaker +{ + Aig *aig; + Cell *cell; + idict<AigNode> aig_indices; + + int the_true_node; + int the_false_node; + + AigMaker(Aig *aig, Cell *cell) : aig(aig), cell(cell) + { + the_true_node = -1; + the_false_node = -1; + } + + int node2index(const AigNode &node) + { + if (node.left_parent > node.right_parent) { + AigNode n(node); + std::swap(n.left_parent, n.right_parent); + return node2index(n); + } + + if (!aig_indices.count(node)) { + aig_indices.expect(node, GetSize(aig->nodes)); + aig->nodes.push_back(node); + } + + return aig_indices.at(node); + } + + int bool_node(bool value) + { + AigNode node; + node.inverter = value; + return node2index(node); + } + + int inport(IdString portname, int portbit = 0, bool inverter = false) + { + if (portbit >= GetSize(cell->getPort(portname))) { + if (cell->parameters.count(portname.str() + "_SIGNED") && cell->getParam(portname.str() + "_SIGNED").as_bool()) + return inport(portname, GetSize(cell->getPort(portname))-1, inverter); + return bool_node(inverter); + } + + AigNode node; + node.portname = portname; + node.portbit = portbit; + node.inverter = inverter; + return node2index(node); + } + + vector<int> inport_vec(IdString portname, int width) + { + vector<int> vec; + for (int i = 0; i < width; i++) + vec.push_back(inport(portname, i)); + return vec; + } + + int not_inport(IdString portname, int portbit = 0) + { + return inport(portname, portbit, true); + } + + int not_gate(int A) + { + AigNode node(aig_indices[A]); + node.outports.clear(); + node.inverter = !node.inverter; + return node2index(node); + } + + int and_gate(int A, int B, bool inverter = false) + { + if (A == B) + return inverter ? not_gate(A) : A; + + const AigNode &nA = aig_indices[A]; + const AigNode &nB = aig_indices[B]; + + AigNode nB_inv(nB); + nB_inv.inverter = !nB_inv.inverter; + + if (nA == nB_inv) + return bool_node(inverter); + + bool nA_bool = nA.portbit < 0 && nA.left_parent < 0 && nA.right_parent < 0; + bool nB_bool = nB.portbit < 0 && nB.left_parent < 0 && nB.right_parent < 0; + + if (nA_bool && nB_bool) { + bool bA = nA.inverter; + bool bB = nB.inverter; + return bool_node(inverter != (bA && bB)); + } + + if (nA_bool) { + bool bA = nA.inverter; + if (inverter) + return bA ? not_gate(B) : bool_node(true); + return bA ? B : bool_node(false); + } + + if (nB_bool) { + bool bB = nB.inverter; + if (inverter) + return bB ? not_gate(A) : bool_node(true); + return bB ? A : bool_node(false); + } + + AigNode node; + node.inverter = inverter; + node.left_parent = A; + node.right_parent = B; + return node2index(node); + } + + int nand_gate(int A, int B) + { + return and_gate(A, B, true); + } + + int or_gate(int A, int B) + { + return nand_gate(not_gate(A), not_gate(B)); + } + + int nor_gate(int A, int B) + { + return and_gate(not_gate(A), not_gate(B)); + } + + int xor_gate(int A, int B) + { + return nor_gate(and_gate(A, B), nor_gate(A, B)); + } + + int xnor_gate(int A, int B) + { + return or_gate(and_gate(A, B), nor_gate(A, B)); + } + + int mux_gate(int A, int B, int S) + { + return or_gate(and_gate(A, not_gate(S)), and_gate(B, S)); + } + + vector<int> adder(const vector<int> &A, const vector<int> &B, int carry, vector<int> *X = nullptr, vector<int> *CO = nullptr) + { + vector<int> Y(GetSize(A)); + log_assert(GetSize(A) == GetSize(B)); + for (int i = 0; i < GetSize(A); i++) { + Y[i] = xor_gate(xor_gate(A[i], B[i]), carry); + carry = or_gate(and_gate(A[i], B[i]), and_gate(or_gate(A[i], B[i]), carry)); + if (X != nullptr) + X->at(i) = xor_gate(A[i], B[i]); + if (CO != nullptr) + CO->at(i) = carry; + } + return Y; + } + + void outport(int node, IdString portname, int portbit = 0) + { + if (portbit < GetSize(cell->getPort(portname))) + aig->nodes.at(node).outports.push_back(pair<IdString, int>(portname, portbit)); + } + + void outport_bool(int node, IdString portname) + { + outport(node, portname); + for (int i = 1; i < GetSize(cell->getPort(portname)); i++) + outport(bool_node(false), portname, i); + } + + void outport_vec(const vector<int> &vec, IdString portname) + { + for (int i = 0; i < GetSize(vec); i++) + outport(vec.at(i), portname, i); + } +}; + +Aig::Aig(Cell *cell) +{ + if (cell->type[0] != '$') + return; + + AigMaker mk(this, cell); + name = cell->type.str(); + + string mkname_last; + bool mkname_a_signed = false; + bool mkname_b_signed = false; + bool mkname_is_signed = false; + + cell->parameters.sort(); + for (auto p : cell->parameters) + { + if (p.first == "\\A_WIDTH" && mkname_a_signed) { + name = mkname_last + stringf(":%d%c", p.second.as_int(), mkname_is_signed ? 'S' : 'U'); + } else if (p.first == "\\B_WIDTH" && mkname_b_signed) { + name = mkname_last + stringf(":%d%c", p.second.as_int(), mkname_is_signed ? 'S' : 'U'); + } else { + mkname_last = name; + name += stringf(":%d", p.second.as_int()); + } + + mkname_a_signed = false; + mkname_b_signed = false; + mkname_is_signed = false; + if (p.first == "\\A_SIGNED") { + mkname_a_signed = true; + mkname_is_signed = p.second.as_bool(); + } + if (p.first == "\\B_SIGNED") { + mkname_b_signed = true; + mkname_is_signed = p.second.as_bool(); + } + } + + if (cell->type.in("$not", "$_NOT_", "$pos", "$_BUF_")) + { + for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) { + int A = mk.inport("\\A", i); + int Y = cell->type.in("$not", "$_NOT_") ? mk.not_gate(A) : A; + mk.outport(Y, "\\Y", i); + } + goto optimize; + } + + if (cell->type.in("$and", "$_AND_", "$_NAND_", "$or", "$_OR_", "$_NOR_", "$xor", "$xnor", "$_XOR_", "$_XNOR_")) + { + for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) { + int A = mk.inport("\\A", i); + int B = mk.inport("\\B", i); + int Y = cell->type.in("$and", "$_AND_") ? mk.and_gate(A, B) : + cell->type.in("$_NAND_") ? mk.nand_gate(A, B) : + cell->type.in("$or", "$_OR_") ? mk.or_gate(A, B) : + cell->type.in("$_NOR_") ? mk.nor_gate(A, B) : + cell->type.in("$xor", "$_XOR_") ? mk.xor_gate(A, B) : + cell->type.in("$xnor", "$_XNOR_") ? mk.xnor_gate(A, B) : -1; + mk.outport(Y, "\\Y", i); + } + goto optimize; + } + + if (cell->type.in("$mux", "$_MUX_")) + { + int S = mk.inport("\\S"); + for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) { + int A = mk.inport("\\A", i); + int B = mk.inport("\\B", i); + int Y = mk.mux_gate(A, B, S); + mk.outport(Y, "\\Y", i); + } + goto optimize; + } + + if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool")) + { + int Y = mk.inport("\\A", 0); + for (int i = 1; i < GetSize(cell->getPort("\\A")); i++) { + int A = mk.inport("\\A", i); + if (cell->type == "$reduce_and") Y = mk.and_gate(A, Y); + if (cell->type == "$reduce_or") Y = mk.or_gate(A, Y); + if (cell->type == "$reduce_bool") Y = mk.or_gate(A, Y); + if (cell->type == "$reduce_xor") Y = mk.xor_gate(A, Y); + if (cell->type == "$reduce_xnor") Y = mk.xor_gate(A, Y); + } + if (cell->type == "$reduce_xnor") + Y = mk.not_gate(Y); + mk.outport(Y, "\\Y", 0); + for (int i = 1; i < GetSize(cell->getPort("\\Y")); i++) + mk.outport(mk.bool_node(false), "\\Y", i); + goto optimize; + } + + if (cell->type.in("$logic_not", "$logic_and", "$logic_or")) + { + int A = mk.inport("\\A", 0), Y = -1; + for (int i = 1; i < GetSize(cell->getPort("\\A")); i++) + A = mk.or_gate(mk.inport("\\A", i), A); + if (cell->type.in("$logic_and", "$logic_or")) { + int B = mk.inport("\\B", 0); + for (int i = 1; i < GetSize(cell->getPort("\\B")); i++) + B = mk.or_gate(mk.inport("\\B", i), B); + if (cell->type == "$logic_and") Y = mk.and_gate(A, B); + if (cell->type == "$logic_or") Y = mk.or_gate(A, B); + } else { + if (cell->type == "$logic_not") Y = mk.not_gate(A); + } + mk.outport_bool(Y, "\\Y"); + goto optimize; + } + + if (cell->type.in("$add", "$sub")) + { + int width = GetSize(cell->getPort("\\Y")); + vector<int> A = mk.inport_vec("\\A", width); + vector<int> B = mk.inport_vec("\\B", width); + int carry = mk.bool_node(false); + if (cell->type == "$sub") { + for (auto &n : B) + n = mk.not_gate(n); + carry = mk.not_gate(carry); + } + vector<int> Y = mk.adder(A, B, carry); + mk.outport_vec(Y, "\\Y"); + goto optimize; + } + + if (cell->type == "$alu") + { + int width = GetSize(cell->getPort("\\Y")); + vector<int> A = mk.inport_vec("\\A", width); + vector<int> B = mk.inport_vec("\\B", width); + int carry = mk.inport("\\CI"); + int binv = mk.inport("\\BI"); + for (auto &n : B) + n = mk.xor_gate(n, binv); + vector<int> X(width), CO(width); + vector<int> Y = mk.adder(A, B, carry, &X, &CO); + for (int i = 0; i < width; i++) + X[i] = mk.xor_gate(A[i], B[i]); + mk.outport_vec(Y, "\\Y"); + mk.outport_vec(X, "\\X"); + mk.outport_vec(CO, "\\CO"); + goto optimize; + } + + if (cell->type.in("$eq", "$ne")) + { + 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); + for (int i = 0; i < width; i++) + Y = mk.or_gate(Y, mk.xor_gate(A[i], B[i])); + if (cell->type == "$eq") + Y = mk.not_gate(Y); + mk.outport_bool(Y, "\\Y"); + goto optimize; + } + + if (cell->type == "$_AOI3_") + { + int A = mk.inport("\\A"); + int B = mk.inport("\\B"); + int C = mk.inport("\\C"); + int Y = mk.nor_gate(mk.and_gate(A, B), C); + mk.outport(Y, "\\Y"); + goto optimize; + } + + if (cell->type == "$_OAI3_") + { + int A = mk.inport("\\A"); + int B = mk.inport("\\B"); + int C = mk.inport("\\C"); + int Y = mk.nand_gate(mk.or_gate(A, B), C); + mk.outport(Y, "\\Y"); + goto optimize; + } + + if (cell->type == "$_AOI4_") + { + int A = mk.inport("\\A"); + int B = mk.inport("\\B"); + int C = mk.inport("\\C"); + int D = mk.inport("\\D"); + int Y = mk.nor_gate(mk.and_gate(A, B), mk.and_gate(C, D)); + mk.outport(Y, "\\Y"); + goto optimize; + } + + if (cell->type == "$_OAI4_") + { + int A = mk.inport("\\A"); + int B = mk.inport("\\B"); + int C = mk.inport("\\C"); + int D = mk.inport("\\D"); + int Y = mk.nand_gate(mk.nor_gate(A, B), mk.nor_gate(C, D)); + mk.outport(Y, "\\Y"); + goto optimize; + } + + name.clear(); + return; + +optimize:; + pool<int> used_old_ids; + vector<AigNode> new_nodes; + dict<int, int> old_to_new_ids; + old_to_new_ids[-1] = -1; + + for (int i = GetSize(nodes)-1; i >= 0; i--) { + if (!nodes[i].outports.empty()) + used_old_ids.insert(i); + if (!used_old_ids.count(i)) + continue; + if (nodes[i].left_parent >= 0) + used_old_ids.insert(nodes[i].left_parent); + if (nodes[i].right_parent >= 0) + used_old_ids.insert(nodes[i].right_parent); + } + + for (int i = 0; i < GetSize(nodes); i++) { + if (!used_old_ids.count(i)) + continue; + nodes[i].left_parent = old_to_new_ids.at(nodes[i].left_parent); + nodes[i].right_parent = old_to_new_ids.at(nodes[i].right_parent); + old_to_new_ids[i] = GetSize(new_nodes); + new_nodes.push_back(nodes[i]); + } + + new_nodes.swap(nodes); +} + +YOSYS_NAMESPACE_END diff --git a/kernel/cellaigs.h b/kernel/cellaigs.h new file mode 100644 index 000000000..1417a614c --- /dev/null +++ b/kernel/cellaigs.h @@ -0,0 +1,52 @@ +/* + * 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. + * + */ + +#ifndef CELLAIGS_H +#define CELLAIGS_H + +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN + +struct AigNode +{ + IdString portname; + int portbit; + bool inverter; + int left_parent, right_parent; + vector<pair<IdString, int>> outports; + + AigNode(); + bool operator==(const AigNode &other) const; + unsigned int hash() const; +}; + +struct Aig +{ + string name; + vector<AigNode> nodes; + Aig(Cell *cell); + + bool operator==(const Aig &other) const; + unsigned int hash() const; +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 60e6606f8..cf7bc2dcf 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -2,11 +2,11 @@ * 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 @@ -85,7 +85,7 @@ struct CellTypes std::vector<RTLIL::IdString> unary_ops = { "$not", "$pos", "$neg", "$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool", - "$logic_not", "$slice", "$lut" + "$logic_not", "$slice", "$lut", "$sop" }; std::vector<RTLIL::IdString> binary_ops = { @@ -95,7 +95,6 @@ struct CellTypes "$add", "$sub", "$mul", "$div", "$mod", "$pow", "$logic_and", "$logic_or", "$concat", "$macc" }; - IdString A = "\\A", B = "\\B", S = "\\S", Y = "\\Y"; IdString P = "\\P", G = "\\G", C = "\\C", X = "\\X"; IdString BI = "\\BI", CI = "\\CI", CO = "\\CO", EN = "\\EN"; @@ -113,14 +112,17 @@ struct CellTypes setup_type("$alu", {A, B, CI, BI}, {X, Y, CO}, true); setup_type("$fa", {A, B, C}, {X, Y}, true); + setup_type("$tribuf", {A, EN}, {Y}, true); + setup_type("$assert", {A, EN}, pool<RTLIL::IdString>(), true); + setup_type("$assume", {A, EN}, pool<RTLIL::IdString>(), true); setup_type("$equiv", {A, B}, {Y}, true); } void setup_internals_mem() { IdString SET = "\\SET", CLR = "\\CLR", CLK = "\\CLK", ARST = "\\ARST", EN = "\\EN"; - IdString Q = "\\Q", D = "\\D", ADDR = "\\ADDR", DATA = "\\DATA"; + IdString Q = "\\Q", D = "\\D", ADDR = "\\ADDR", DATA = "\\DATA", RD_EN = "\\RD_EN"; IdString RD_CLK = "\\RD_CLK", RD_ADDR = "\\RD_ADDR", WR_CLK = "\\WR_CLK", WR_EN = "\\WR_EN"; IdString WR_ADDR = "\\WR_ADDR", WR_DATA = "\\WR_DATA", RD_DATA = "\\RD_DATA"; IdString CTRL_IN = "\\CTRL_IN", CTRL_OUT = "\\CTRL_OUT"; @@ -133,16 +135,23 @@ struct CellTypes setup_type("$dlatch", {EN, D}, {Q}); setup_type("$dlatchsr", {EN, SET, CLR, D}, {Q}); - setup_type("$memrd", {CLK, ADDR}, {DATA}); + setup_type("$memrd", {CLK, EN, ADDR}, {DATA}); setup_type("$memwr", {CLK, EN, ADDR, DATA}, pool<RTLIL::IdString>()); - setup_type("$mem", {RD_CLK, RD_ADDR, WR_CLK, WR_EN, WR_ADDR, WR_DATA}, {RD_DATA}); + setup_type("$meminit", {ADDR, DATA}, pool<RTLIL::IdString>()); + setup_type("$mem", {RD_CLK, RD_EN, RD_ADDR, WR_CLK, WR_EN, WR_ADDR, WR_DATA}, {RD_DATA}); setup_type("$fsm", {CLK, ARST, CTRL_IN}, {CTRL_OUT}); } void setup_stdcells() { - IdString A = "\\A", B = "\\B", C = "\\C", D = "\\D", S = "\\S", Y = "\\Y"; + IdString A = "\\A", B = "\\B", C = "\\C", D = "\\D"; + IdString E = "\\E", F = "\\F", G = "\\G", H = "\\H"; + IdString I = "\\I", J = "\\J", K = "\\K", L = "\\L"; + IdString M = "\\I", N = "\\N", O = "\\O", P = "\\P"; + IdString S = "\\S", T = "\\T", U = "\\U", V = "\\V"; + IdString Y = "\\Y"; + setup_type("$_BUF_", {A}, {Y}, true); setup_type("$_NOT_", {A}, {Y}, true); setup_type("$_AND_", {A, B}, {Y}, true); @@ -152,10 +161,14 @@ struct CellTypes setup_type("$_XOR_", {A, B}, {Y}, true); setup_type("$_XNOR_", {A, B}, {Y}, true); setup_type("$_MUX_", {A, B, S}, {Y}, true); + setup_type("$_MUX4_", {A, B, C, D, S, T}, {Y}, true); + setup_type("$_MUX8_", {A, B, C, D, E, F, G, H, S, T, U}, {Y}, true); + setup_type("$_MUX16_", {A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, S, T, U, V}, {Y}, true); setup_type("$_AOI3_", {A, B, C}, {Y}, true); setup_type("$_OAI3_", {A, B, C}, {Y}, true); setup_type("$_AOI4_", {A, B, C, D}, {Y}, true); setup_type("$_OAI4_", {A, B, C, D}, {Y}, true); + setup_type("$_TBUF_", {A, E}, {Y}, true); } void setup_stdcells_mem() @@ -344,6 +357,44 @@ struct CellTypes return t; } + if (cell->type == "$sop") + { + int width = cell->parameters.at("\\WIDTH").as_int(); + int depth = cell->parameters.at("\\DEPTH").as_int(); + std::vector<RTLIL::State> t = cell->parameters.at("\\TABLE").bits; + + while (GetSize(t) < width*depth*2) + t.push_back(RTLIL::S0); + + RTLIL::State default_ret = State::S0; + + for (int i = 0; i < depth; i++) + { + bool match = true; + bool match_x = true; + + for (int j = 0; j < width; j++) { + RTLIL::State a = arg1.bits.at(j); + if (t.at(2*width*i + 2*j + 0) == State::S1) { + if (a == State::S1) match_x = false; + if (a != State::S0) match = false; + } + if (t.at(2*width*i + 2*j + 1) == State::S1) { + if (a == State::S0) match_x = false; + if (a != State::S1) match = false; + } + } + + if (match) + return State::S1; + + if (match_x) + default_ret = State::Sx; + } + + return default_ret; + } + bool signed_a = cell->parameters.count("\\A_SIGNED") > 0 && cell->parameters["\\A_SIGNED"].as_bool(); bool signed_b = cell->parameters.count("\\B_SIGNED") > 0 && cell->parameters["\\B_SIGNED"].as_bool(); int result_len = cell->parameters.count("\\Y_WIDTH") > 0 ? cell->parameters["\\Y_WIDTH"].as_int() : -1; diff --git a/kernel/consteval.h b/kernel/consteval.h index c2e9710fb..4d48b45ea 100644 --- a/kernel/consteval.h +++ b/kernel/consteval.h @@ -2,11 +2,11 @@ * 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 diff --git a/kernel/cost.h b/kernel/cost.h index c6c631e0a..4f12889f4 100644 --- a/kernel/cost.h +++ b/kernel/cost.h @@ -2,11 +2,11 @@ * 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 diff --git a/kernel/driver.cc b/kernel/driver.cc index 116df542c..45cdd461d 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -2,11 +2,11 @@ * 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 @@ -72,6 +72,53 @@ int getopt(int argc, char **argv, const char *optstring) USING_YOSYS_NAMESPACE +#ifdef EMSCRIPTEN +# include <sys/stat.h> +# include <sys/types.h> + +extern "C" int main(int, char**); +extern "C" void run(const char*); +extern "C" const char *errmsg(); +extern "C" const char *prompt(); + +int main(int, char**) +{ + mkdir("/work", 0777); + chdir("/work"); + log_files.push_back(stdout); + log_error_stderr = true; + yosys_banner(); + yosys_setup(); +} + +void run(const char *command) +{ + int selSize = GetSize(yosys_get_design()->selection_stack); + try { + log_last_error = "Internal error (see JavaScript console for details)"; + run_pass(command); + log_last_error = ""; + } catch (...) { + while (GetSize(yosys_get_design()->selection_stack) > selSize) + yosys_get_design()->selection_stack.pop_back(); + throw; + } +} + +const char *errmsg() +{ + return log_last_error.c_str(); +} + +const char *prompt() +{ + const char *p = create_prompt(yosys_get_design(), 0); + while (*p == '\n') p++; + return p; +} + +#else /* EMSCRIPTEN */ + int main(int argc, char **argv) { std::string frontend_command = "auto"; @@ -136,8 +183,8 @@ int main(int argc, char **argv) printf(" -b backend\n"); printf(" use this backend for the output file specified on the command line\n"); printf("\n"); - printf(" -f backend\n"); - printf(" use the specified front for the input files on the command line\n"); + printf(" -f frontend\n"); + printf(" use the specified frontend for the input files on the command line\n"); printf("\n"); printf(" -H\n"); printf(" print the command list\n"); @@ -166,11 +213,16 @@ int main(int argc, char **argv) printf(" -A\n"); printf(" will call abort() at the end of the script. for debugging\n"); printf("\n"); + printf(" -D <header_id>[:<filename>]\n"); + printf(" dump the design when printing the specified log header to a file.\n"); + printf(" yosys_dump_<header_id>.il is used as filename if none is specified.\n"); + printf(" Use 'ALL' as <header_id> to dump at every header.\n"); + printf("\n"); printf(" -V\n"); printf(" print version information and exit\n"); printf("\n"); printf("The option -S is an shortcut for calling the \"synth\" command, a default\n"); - printf("script for transforming the verilog input to a gate-level netlist. For example:\n"); + printf("script for transforming the Verilog input to a gate-level netlist. For example:\n"); printf("\n"); printf(" yosys -o output.blif -S input.v\n"); printf("\n"); @@ -186,7 +238,7 @@ int main(int argc, char **argv) } int opt; - while ((opt = getopt(argc, argv, "MXAQTVSm:f:Hh:b:o:p:l:L:qv:tds:c:")) != -1) + while ((opt = getopt(argc, argv, "MXAQTVSm:f:Hh:b:o:p:l:L:qv:tds:c:D:")) != -1) { switch (opt) { @@ -268,6 +320,28 @@ int main(int argc, char **argv) scriptfile = optarg; scriptfile_tcl = true; break; + case 'D': + { + auto args = split_tokens(optarg, ":"); + if (!args.empty() && args[0] == "ALL") { + if (GetSize(args) != 1) { + fprintf(stderr, "Invalid number of tokens in -D ALL.\n"); + exit(1); + } + log_hdump_all = true; + } else { + if (!args.empty() && !args[0].empty() && args[0].back() == '.') + args[0].pop_back(); + if (GetSize(args) == 1) + args.push_back("yosys_dump_" + args[0] + ".il"); + if (GetSize(args) != 2) { + fprintf(stderr, "Invalid number of tokens in -D.\n"); + exit(1); + } + log_hdump[args[0]].insert(args[1]); + } + } + break; default: fprintf(stderr, "Run '%s -h' for help.\n", argv[0]); exit(1); @@ -357,7 +431,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) { @@ -440,3 +514,5 @@ int main(int argc, char **argv) return 0; } +#endif /* EMSCRIPTEN */ + diff --git a/kernel/hashlib.h b/kernel/hashlib.h index c96023920..3c824b8c3 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -1,5 +1,5 @@ // This is free and unencumbered software released into the public domain. -// +// // Anyone is free to copy, modify, publish, use, compile, sell, or // distribute this software, either in source code form or as a compiled // binary, for any purpose, commercial or non-commercial, and by any @@ -10,6 +10,7 @@ // ------------------------------------------------------- #ifndef HASHLIB_H +#define HASHLIB_H #include <stdexcept> #include <algorithm> @@ -30,7 +31,7 @@ inline unsigned int mkhash(unsigned int a, unsigned int b) { const unsigned int mkhash_init = 5381; // The ADD version of DJB2 -// (usunsigned int mkhashe this version for cache locality in b) +// (use this version for cache locality in b) inline unsigned int mkhash_add(unsigned int a, unsigned int b) { return ((a << 5) + a) + b; } @@ -58,16 +59,25 @@ template<typename T> struct hash_ops { } }; -template<> struct hash_ops<int> { +struct hash_int_ops { template<typename T> static inline bool cmp(T a, T b) { return a == b; } - template<typename T> - static inline unsigned int hash(T a) { +}; + +template<> struct hash_ops<int32_t> : hash_int_ops +{ + static inline unsigned int hash(int32_t a) { return a; } }; +template<> struct hash_ops<int64_t> : hash_int_ops +{ + static inline unsigned int hash(int64_t a) { + return mkhash((unsigned int)(a), (unsigned int)(a >> 32)); + } +}; template<> struct hash_ops<std::string> { static inline bool cmp(const std::string &a, const std::string &b) { @@ -86,9 +96,22 @@ 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)); + } +}; + +template<typename... T> struct hash_ops<std::tuple<T...>> { + static inline bool cmp(std::tuple<T...> a, std::tuple<T...> b) { + return a == b; + } + template<size_t I = 0> + static inline typename std::enable_if<I == sizeof...(T), unsigned int>::type hash(std::tuple<T...>) { + return mkhash_init; + } + template<size_t I = 0> + static inline typename std::enable_if<I != sizeof...(T), unsigned int>::type hash(std::tuple<T...> 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))); } }; @@ -97,10 +120,9 @@ template<typename T> struct hash_ops<std::vector<T>> { return a == b; } static inline unsigned int hash(std::vector<T> a) { - hash_ops<T> t_ops; unsigned int h = mkhash_init; for (auto k : a) - h = mkhash(h, t_ops.hash(k)); + h = mkhash(h, hash_ops<T>::hash(k)); return h; } }; @@ -115,8 +137,8 @@ struct hash_cstr_ops { static inline unsigned int hash(const char *a) { unsigned int hash = mkhash_init; while (*a) - hash = mkhash(hash, *(a++)); - return hash; + hash = mkhash(hash, *(a++)); + return hash; } }; @@ -135,10 +157,15 @@ struct hash_obj_ops { } template<typename T> static inline unsigned int hash(const T *a) { - return a->hash(); + return a ? a->hash() : 0; } }; +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 = { @@ -167,6 +194,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 @@ -178,18 +206,19 @@ class dict entry_t() { } entry_t(const std::pair<K, T> &udata, int next) : udata(udata), next(next) { } + entry_t(std::pair<K, T> &&udata, int next) : udata(std::move(udata)), next(next) { } }; std::vector<int> hashtable; std::vector<entry_t> entries; OPS ops; -#if 0 +#ifdef NDEBUG + static inline void do_assert(bool) { } +#else static inline void do_assert(bool cond) { if (!cond) throw std::runtime_error("dict<> assert failed."); } -#else - static inline void do_assert(bool) { } #endif int do_hash(const K &key) const @@ -203,7 +232,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())); @@ -220,6 +249,8 @@ class dict return 0; int k = hashtable[hash]; + do_assert(0 <= k && k < int(entries.size())); + if (k == index) { hashtable[hash] = entries[index].next; } else { @@ -237,6 +268,8 @@ class dict int back_hash = do_hash(entries[back_idx].udata.first); k = hashtable[back_hash]; + do_assert(0 <= k && k < int(entries.size())); + if (k == back_idx) { hashtable[back_hash] = index; } else { @@ -278,6 +311,19 @@ class dict return index; } + int do_insert(const K &key, int &hash) + { + if (hashtable.empty()) { + entries.push_back(entry_t(std::pair<K, T>(key, T()), -1)); + do_rehash(); + hash = do_hash(key); + } else { + entries.push_back(entry_t(std::pair<K, T>(key, T()), hashtable[hash])); + hashtable[hash] = entries.size() - 1; + } + return entries.size() - 1; + } + int do_insert(const std::pair<K, T> &value, int &hash) { if (hashtable.empty()) { @@ -375,6 +421,16 @@ public: insert(*first); } + std::pair<iterator, bool> insert(const K &key) + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + if (i >= 0) + return std::pair<iterator, bool>(iterator(this, i), false); + i = do_insert(key, hash); + return std::pair<iterator, bool>(iterator(this, i), true); + } + std::pair<iterator, bool> insert(const std::pair<K, T> &value) { int hash = do_hash(value.first); @@ -449,6 +505,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); @@ -476,16 +541,17 @@ public: return false; for (auto &it : entries) { auto oit = other.find(it.udata.first); - if (oit == other.end() || oit->second != it.udata.second) + if (oit == other.end() || !(oit->second == it.udata.second)) return false; } return true; } bool operator!=(const dict &other) const { - return !(*this == other); + 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(); } @@ -516,12 +582,12 @@ protected: std::vector<entry_t> entries; OPS ops; -#if 0 +#ifdef NDEBUG + static inline void do_assert(bool) { } +#else static inline void do_assert(bool cond) { if (!cond) throw std::runtime_error("pool<> assert failed."); } -#else - static inline void do_assert(bool) { } #endif int do_hash(const K &key) const @@ -535,7 +601,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())); @@ -775,6 +841,14 @@ public: do_rehash(); } + K pop() + { + iterator it = begin(); + K ret = *it; + erase(it); + return ret; + } + void swap(pool &other) { hashtable.swap(other.hashtable); @@ -791,9 +865,10 @@ public: } bool operator!=(const pool &other) const { - return !(*this == other); + 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(); } @@ -831,6 +906,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); @@ -850,6 +934,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/log.cc b/kernel/log.cc index ada2cabb2..fe84184a5 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -2,11 +2,11 @@ * 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 @@ -40,6 +40,8 @@ YOSYS_NAMESPACE_BEGIN std::vector<FILE*> log_files; std::vector<std::ostream*> log_streams; +std::map<std::string, std::set<std::string>> log_hdump; +bool log_hdump_all = false; FILE *log_errfile = NULL; SHA1 *log_hasher = NULL; @@ -48,10 +50,11 @@ bool log_error_stderr = false; bool log_cmd_error_throw = false; bool log_quiet_warnings = false; int log_verbose_level; +string log_last_error; vector<int> header_count; pool<RTLIL::IdString> log_id_cache; -vector<string> string_buf; +vector<shared_str> string_buf; int string_buf_index = -1; static struct timeval initial_tv = { 0, 0 }; @@ -135,7 +138,7 @@ void logv(const char *format, va_list ap) *f << str; } -void logv_header(const char *format, va_list ap) +void logv_header(RTLIL::Design *design, const char *format, va_list ap) { bool pop_errfile = false; @@ -148,12 +151,24 @@ void logv_header(const char *format, va_list ap) pop_errfile = true; } + std::string header_id; + for (int c : header_count) - log("%d.", c); - log(" "); + header_id += stringf("%s%d", header_id.empty() ? "" : ".", c); + + log("%s. ", header_id.c_str()); logv(format, ap); log_flush(); + if (log_hdump_all) + log_hdump[header_id].insert("yosys_dump_" + header_id + ".il"); + + if (log_hdump.count(header_id) && design != nullptr) + for (auto &filename : log_hdump.at(header_id)) { + log("Dumping current design to '%s'.\n", filename.c_str()); + Pass::call(design, {"dump", "-o", filename}); + } + if (pop_errfile) log_files.pop_back(); } @@ -173,6 +188,10 @@ void logv_warning(const char *format, va_list ap) void logv_error(const char *format, va_list ap) { +#ifdef EMSCRIPTEN + auto backup_log_files = log_files; +#endif + if (log_errfile != NULL) log_files.push_back(log_errfile); @@ -181,10 +200,16 @@ void logv_error(const char *format, va_list ap) if (f == stdout) f = stderr; - log("ERROR: "); - logv(format, ap); + log_last_error = vstringf(format, ap); + log("ERROR: %s", log_last_error.c_str()); log_flush(); + +#ifdef EMSCRIPTEN + log_files = backup_log_files; + throw 0; +#else exit(1); +#endif } void log(const char *format, ...) @@ -195,11 +220,11 @@ void log(const char *format, ...) va_end(ap); } -void log_header(const char *format, ...) +void log_header(RTLIL::Design *design, const char *format, ...) { va_list ap; va_start(ap, format); - logv_header(format, ap); + logv_header(design, format, ap); va_end(ap); } @@ -224,8 +249,8 @@ void log_cmd_error(const char *format, ...) va_start(ap, format); if (log_cmd_error_throw) { - log("ERROR: "); - logv(format, ap); + log_last_error = vstringf(format, ap); + log("ERROR: %s", log_last_error.c_str()); log_flush(); throw log_cmd_error_exception(); } @@ -253,7 +278,7 @@ void log_pop() log_flush(); } -#ifdef __linux__ +#if defined(__linux__) && defined(YOSYS_ENABLE_PLUGINS) void log_backtrace(const char *prefix, int levels) { if (levels <= 0) return; @@ -365,6 +390,10 @@ void log_flush() f->flush(); } +void log_dump_val_worker(RTLIL::IdString v) { + log("%s", log_id(v)); +} + void log_dump_val_worker(RTLIL::SigSpec v) { log("%s", log_signal(v)); } diff --git a/kernel/log.h b/kernel/log.h index fd35c7bf7..33e624dcb 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -2,11 +2,11 @@ * 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 @@ -29,6 +29,11 @@ # include <sys/resource.h> #endif +#if defined(_MSC_VER) +// At least this is not in MSVC++ 2013. +# define __PRETTY_FUNCTION__ __FUNCTION__ +#endif + // from libs/sha1/sha1.h class SHA1; @@ -42,6 +47,8 @@ struct log_cmd_error_exception { }; extern std::vector<FILE*> log_files; extern std::vector<std::ostream*> log_streams; +extern std::map<std::string, std::set<std::string>> log_hdump; +extern bool log_hdump_all; extern FILE *log_errfile; extern SHA1 *log_hasher; @@ -50,14 +57,15 @@ extern bool log_error_stderr; extern bool log_cmd_error_throw; extern bool log_quiet_warnings; extern int log_verbose_level; +extern string log_last_error; void logv(const char *format, va_list ap); -void logv_header(const char *format, va_list ap); +void logv_header(RTLIL::Design *design, const char *format, va_list ap); void logv_warning(const char *format, va_list ap); YS_NORETURN void logv_error(const char *format, va_list ap) YS_ATTRIBUTE(noreturn); void log(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); -void log_header(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); +void log_header(RTLIL::Design *design, const char *format, ...) YS_ATTRIBUTE(format(printf, 2, 3)); void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); YS_NORETURN void log_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2), noreturn); YS_NORETURN void log_cmd_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2), noreturn); @@ -144,7 +152,7 @@ std::string cover_list_worker(std::string prefix, std::string first, T... rest) // ------------------------------------------------------------ // simple timer for performance measurements -// toggle the '#if 1' to get a baseline for the perormance penalty added by the measurement +// toggle the '#if 1' to get a baseline for the performance penalty added by the measurement struct PerformanceTimer { #if 1 @@ -155,11 +163,13 @@ struct PerformanceTimer } static int64_t query() { -#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) +# if _WIN32 + return 0; +# elif defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) struct timespec ts; clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); return int64_t(ts.tv_sec)*1000000000 + ts.tv_nsec; -#elif defined(RUSAGE_SELF) +# elif defined(RUSAGE_SELF) struct rusage rusage; int64_t t; if (getrusage(RUSAGE_SELF, &rusage) == -1) { @@ -169,11 +179,9 @@ struct PerformanceTimer t = 1000000000ULL * (int64_t) rusage.ru_utime.tv_sec + (int64_t) rusage.ru_utime.tv_usec * 1000ULL; t += 1000000000ULL * (int64_t) rusage.ru_stime.tv_sec + (int64_t) rusage.ru_stime.tv_usec * 1000ULL; return t; -#elif _WIN32 - return 0; -#else - #error Dont know how to measure per-process CPU time. Need alternative method (times()/clocks()/gettimeofday()?). -#endif +# else +# error Dont know how to measure per-process CPU time. Need alternative method (times()/clocks()/gettimeofday()?). +# endif } void reset() { @@ -221,8 +229,35 @@ static inline void log_dump_val_worker(const char *v) { log("%s", v); } static inline void log_dump_val_worker(std::string v) { log("%s", v.c_str()); } static inline void log_dump_val_worker(PerformanceTimer p) { log("%f seconds", p.sec()); } static inline void log_dump_args_worker(const char *p YS_ATTRIBUTE(unused)) { log_assert(*p == 0); } +void log_dump_val_worker(RTLIL::IdString v); void log_dump_val_worker(RTLIL::SigSpec v); +template<typename K, typename T, typename OPS> +static inline void log_dump_val_worker(dict<K, T, OPS> &v) { + log("{"); + bool first = true; + for (auto &it : v) { + log(first ? " " : ", "); + log_dump_val_worker(it.first); + log(": "); + log_dump_val_worker(it.second); + first = false; + } + log(" }"); +} + +template<typename K, typename OPS> +static inline void log_dump_val_worker(pool<K, OPS> &v) { + log("{"); + bool first = true; + for (auto &it : v) { + log(first ? " " : ", "); + log_dump_val_worker(it); + first = false; + } + log(" }"); +} + template<typename T> static inline void log_dump_val_worker(T *ptr) { log("%p", ptr); } diff --git a/kernel/macc.h b/kernel/macc.h index cac5b00d7..286ce567f 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -2,11 +2,11 @@ * 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 @@ -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 69c13bd3b..ffcb48d44 100644 --- a/kernel/modtools.h +++ b/kernel/modtools.h @@ -2,11 +2,11 @@ * 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 @@ -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 56dc695aa..115880ed6 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -2,11 +2,11 @@ * 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 @@ -18,6 +18,8 @@ */ #include "kernel/yosys.h" +#include "kernel/satgen.h" + #include <string.h> #include <stdlib.h> #include <stdio.h> @@ -78,6 +80,7 @@ Pass::pre_post_exec_state_t Pass::pre_execute() state.begin_ns = PerformanceTimer::query(); state.parent_pass = current_pass; current_pass = this; + clear_flags(); return state; } @@ -97,6 +100,10 @@ void Pass::help() log("\n"); } +void Pass::clear_flags() +{ +} + void Pass::cmd_log_args(const std::vector<std::string> &args) { if (args.size() <= 1) @@ -148,7 +155,7 @@ void Pass::call(RTLIL::Design *design, std::string command) std::vector<std::string> args; std::string cmd_buf = command; - std::string tok = next_token(cmd_buf, " \t\r\n"); + std::string tok = next_token(cmd_buf, " \t\r\n", true); if (tok.empty()) return; @@ -158,7 +165,7 @@ void Pass::call(RTLIL::Design *design, std::string command) while (!cmd_buf.empty() && (cmd_buf.back() == ' ' || cmd_buf.back() == '\t' || cmd_buf.back() == '\r' || cmd_buf.back() == '\n')) cmd_buf.resize(cmd_buf.size()-1); - log_header("Shell command: %s\n", cmd_buf.c_str()); + log_header(design, "Shell command: %s\n", cmd_buf.c_str()); int retCode = run_command(cmd_buf); if (retCode != 0) log_cmd_error("Shell command returned error code %d.\n", retCode); @@ -199,7 +206,7 @@ void Pass::call(RTLIL::Design *design, std::string command) call(design, args); args.clear(); } - tok = next_token(cmd_buf, " \t\r\n"); + tok = next_token(cmd_buf, " \t\r\n", true); } call(design, args); @@ -207,7 +214,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) { @@ -280,6 +287,60 @@ void Pass::call_on_module(RTLIL::Design *design, RTLIL::Module *module, std::vec design->selected_active_module = backup_selected_active_module; } +bool ScriptPass::check_label(std::string label, std::string info) +{ + if (active_design == nullptr) { + log("\n"); + if (info.empty()) + log(" %s:\n", label.c_str()); + else + log(" %s: %s\n", label.c_str(), info.c_str()); + return true; + } else { + if (!active_run_from.empty() && active_run_from == active_run_to) { + block_active = (label == active_run_from); + } else { + if (label == active_run_from) + block_active = true; + if (label == active_run_to) + block_active = false; + } + return block_active; + } +} + +void ScriptPass::run(std::string command, std::string info) +{ + if (active_design == nullptr) { + if (info.empty()) + log(" %s\n", command.c_str()); + else + log(" %s %s\n", command.c_str(), info.c_str()); + } else + Pass::call(active_design, command); +} + +void ScriptPass::run_script(RTLIL::Design *design, std::string run_from, std::string run_to) +{ + help_mode = false; + active_design = design; + block_active = run_from.empty(); + active_run_from = run_from; + active_run_to = run_to; + script(); +} + +void ScriptPass::help_script() +{ + clear_flags(); + help_mode = true; + active_design = nullptr; + block_active = true; + active_run_from.clear(); + active_run_to.clear(); + script(); +} + Frontend::Frontend(std::string name, std::string short_help) : Pass(name.rfind("=", 0) == 0 ? name.substr(1) : "read_" + name, short_help), frontend_name(name.rfind("=", 0) == 0 ? name.substr(1) : name) @@ -357,8 +418,7 @@ void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector<s } f = new std::istringstream(last_here_document); } else { - if (filename.substr(0, 2) == "+/") - filename = proc_share_dirname() + filename.substr(1); + rewrite_filename(filename); std::ifstream *ff = new std::ifstream; ff->open(filename.c_str()); if (ff->fail()) @@ -533,23 +593,36 @@ 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) { - size_t pos = 0; - while ((pos = tex.find('_', pos)) != std::string::npos) { + for (size_t pos = 0; (pos = tex.find('_', pos)) != std::string::npos; pos += 2) tex.replace(pos, 1, "\\_"); - pos += 2; - } + for (size_t pos = 0; (pos = tex.find('$', pos)) != std::string::npos; pos += 2) + tex.replace(pos, 1, "\\$"); } void write_tex(FILE *f, std::string cmd, std::string title, std::string text) { @@ -609,6 +682,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 +698,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,17 +735,27 @@ 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; } help(); } } HelpPass; - + struct EchoPass : public Pass { EchoPass() : Pass("echo", "turning echoing back of commands on and off") { } virtual void help() @@ -692,6 +788,18 @@ struct EchoPass : public Pass { log("echo %s\n", echo_mode ? "on" : "off"); } } EchoPass; - + +SatSolver *yosys_satsolver_list; +SatSolver *yosys_satsolver; + +struct MinisatSatSolver : public SatSolver { + MinisatSatSolver() : SatSolver("minisat") { + yosys_satsolver = this; + } + virtual ezSAT *create() YS_OVERRIDE { + return new ezMiniSAT(); + } +} MinisatSatSolver; + YOSYS_NAMESPACE_END diff --git a/kernel/register.h b/kernel/register.h index 71ab6ea6e..8024c56a0 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -2,11 +2,11 @@ * 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 @@ -31,6 +31,7 @@ struct Pass virtual ~Pass(); virtual void help(); + virtual void clear_flags(); virtual void execute(std::vector<std::string> args, RTLIL::Design *design) = 0; int call_counter; @@ -63,6 +64,22 @@ struct Pass static void done_register(); }; +struct ScriptPass : Pass +{ + bool block_active, help_mode; + RTLIL::Design *active_design; + std::string active_run_from, active_run_to; + + ScriptPass(std::string name, std::string short_help = "** document me **") : Pass(name, short_help) { } + + virtual void script() = 0; + + bool check_label(std::string label, std::string info = std::string()); + void run(std::string command, std::string info = std::string()); + void run_script(RTLIL::Design *design, std::string run_from = std::string(), std::string run_to = std::string()); + void help_script(); +}; + struct Frontend : Pass { // for reading of here documents @@ -71,7 +88,7 @@ struct Frontend : Pass std::string frontend_name; Frontend(std::string name, std::string short_help = "** document me **"); - virtual void run_register(); + virtual void run_register() YS_OVERRIDE; virtual ~Frontend(); virtual void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE YS_FINAL; virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) = 0; @@ -87,7 +104,7 @@ struct Backend : Pass { std::string backend_name; Backend(std::string name, std::string short_help = "** document me **"); - virtual void run_register(); + virtual void run_register() YS_OVERRIDE; virtual ~Backend(); virtual void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE YS_FINAL; virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) = 0; diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 776625b9c..9da6d2816 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -2,11 +2,11 @@ * 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 @@ -147,7 +147,7 @@ RTLIL::Const RTLIL::Const::from_string(std::string str) std::string RTLIL::Const::decode_string() const { std::string string; - std::vector <char> string_chars; + std::vector<char> string_chars; for (int i = 0; i < int (bits.size()); i += 8) { char ch = 0; for (int j = 0; j < 8 && i + j < int (bits.size()); j++) @@ -161,6 +161,46 @@ std::string RTLIL::Const::decode_string() const return string; } +void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id) +{ + attributes[id] = RTLIL::Const(1); +} + +bool RTLIL::AttrObject::get_bool_attribute(RTLIL::IdString id) const +{ + if (attributes.count(id) == 0) + return false; + return attributes.at(id).as_bool(); +} + +void RTLIL::AttrObject::set_strpool_attribute(RTLIL::IdString id, const pool<string> &data) +{ + string attrval; + for (auto &s : data) { + if (!attrval.empty()) + attrval += "|"; + attrval += s; + } + attributes[id] = RTLIL::Const(attrval); +} + +void RTLIL::AttrObject::add_strpool_attribute(RTLIL::IdString id, const pool<string> &data) +{ + pool<string> union_data = get_strpool_attribute(id); + union_data.insert(data.begin(), data.end()); + if (!union_data.empty()) + set_strpool_attribute(id, union_data); +} + +pool<string> RTLIL::AttrObject::get_strpool_attribute(RTLIL::IdString id) const +{ + pool<string> data; + if (attributes.count(id) != 0) + for (auto s : split_tokens(attributes.at(id).decode_string(), "|")) + data.insert(s); + return data; +} + bool RTLIL::Selection::selected_module(RTLIL::IdString mod_name) const { if (full_selection) @@ -264,6 +304,8 @@ RTLIL::Design::~Design() { for (auto it = modules_.begin(); it != modules_.end(); ++it) delete it->second; + for (auto n : verilog_packages) + delete n; } RTLIL::ObjRange<RTLIL::Module*> RTLIL::Design::modules() @@ -276,6 +318,21 @@ RTLIL::Module *RTLIL::Design::module(RTLIL::IdString name) return modules_.count(name) ? modules_.at(name) : NULL; } +RTLIL::Module *RTLIL::Design::top_module() +{ + RTLIL::Module *module = nullptr; + int module_count = 0; + + for (auto mod : selected_modules()) { + if (mod->get_bool_attribute("\\top")) + return mod; + module_count++; + module = mod; + } + + return module_count == 1 ? module : nullptr; +} + void RTLIL::Design::add(RTLIL::Module *module) { log_assert(modules_.count(module->name) == 0); @@ -389,6 +446,13 @@ void RTLIL::Design::remove(RTLIL::Module *module) delete module; } +void RTLIL::Design::rename(RTLIL::Module *module, RTLIL::IdString new_name) +{ + modules_.erase(module->name); + module->name = new_name; + add(module); +} + void RTLIL::Design::sort() { scratchpad.sort(); @@ -783,6 +847,15 @@ namespace { return; } + if (cell->type == "$sop") { + param("\\DEPTH"); + param("\\TABLE"); + port("\\A", param("\\WIDTH")); + port("\\Y", 1); + check_expected(); + return; + } + if (cell->type == "$sr") { param_bool("\\SET_POLARITY"); param_bool("\\CLR_POLARITY"); @@ -885,6 +958,7 @@ namespace { param_bool("\\CLK_POLARITY"); param_bool("\\TRANSPARENT"); port("\\CLK", 1); + port("\\EN", 1); port("\\ADDR", param("\\ABITS")); port("\\DATA", param("\\WIDTH")); check_expected(); @@ -904,16 +978,27 @@ namespace { return; } + if (cell->type == "$meminit") { + param("\\MEMID"); + param("\\PRIORITY"); + port("\\ADDR", param("\\ABITS")); + port("\\DATA", param("\\WIDTH") * param("\\WORDS")); + check_expected(); + return; + } + if (cell->type == "$mem") { param("\\MEMID"); param("\\SIZE"); param("\\OFFSET"); - param_bits("\\RD_CLK_ENABLE", param("\\RD_PORTS")); - param_bits("\\RD_CLK_POLARITY", param("\\RD_PORTS")); - param_bits("\\RD_TRANSPARENT", param("\\RD_PORTS")); - param_bits("\\WR_CLK_ENABLE", param("\\WR_PORTS")); - param_bits("\\WR_CLK_POLARITY", param("\\WR_PORTS")); + param("\\INIT"); + 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")); port("\\RD_DATA", param("\\RD_PORTS") * param("\\WIDTH")); port("\\WR_CLK", param("\\WR_PORTS")); @@ -924,6 +1009,14 @@ namespace { return; } + if (cell->type == "$tribuf") { + port("\\A", param("\\WIDTH")); + port("\\Y", param("\\WIDTH")); + port("\\EN", 1); + check_expected(); + return; + } + if (cell->type == "$assert") { port("\\A", 1); port("\\EN", 1); @@ -931,6 +1024,13 @@ namespace { return; } + if (cell->type == "$assume") { + port("\\A", 1); + port("\\EN", 1); + check_expected(); + return; + } + if (cell->type == "$equiv") { port("\\A", 1); port("\\B", 1); @@ -953,6 +1053,12 @@ namespace { if (cell->type == "$_AOI4_") { check_gate("ABCDY"); return; } if (cell->type == "$_OAI4_") { check_gate("ABCDY"); return; } + if (cell->type == "$_TBUF_") { check_gate("AYE"); return; } + + if (cell->type == "$_MUX4_") { check_gate("ABCDSTY"); return; } + if (cell->type == "$_MUX8_") { check_gate("ABCDEFGHSTUY"); return; } + if (cell->type == "$_MUX16_") { check_gate("ABCDEFGHIJKLMNOPSTUVY"); return; } + if (cell->type == "$_SR_NN_") { check_gate("SRQ"); return; } if (cell->type == "$_SR_NP_") { check_gate("SRQ"); return; } if (cell->type == "$_SR_PN_") { check_gate("SRQ"); return; } @@ -1097,6 +1203,8 @@ void RTLIL::Module::cloneInto(RTLIL::Module *new_mod) const log_assert(new_mod->refcount_wires_ == 0); log_assert(new_mod->refcount_cells_ == 0); + new_mod->avail_parameters = avail_parameters; + for (auto &conn : connections_) new_mod->connect(conn); @@ -1351,6 +1459,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); @@ -1491,10 +1612,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") @@ -1509,11 +1630,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 @@ -1592,6 +1713,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) @@ -1645,13 +1767,23 @@ RTLIL::Cell* RTLIL::Module::addConcat(RTLIL::IdString name, RTLIL::SigSpec sig_a return cell; } -RTLIL::Cell* RTLIL::Module::addLut(RTLIL::IdString name, RTLIL::SigSpec sig_i, RTLIL::SigSpec sig_o, RTLIL::Const lut) +RTLIL::Cell* RTLIL::Module::addLut(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const lut) { RTLIL::Cell *cell = addCell(name, "$lut"); cell->parameters["\\LUT"] = lut; - cell->parameters["\\WIDTH"] = sig_i.size(); - cell->setPort("\\A", sig_i); - cell->setPort("\\Y", sig_o); + cell->parameters["\\WIDTH"] = sig_a.size(); + cell->setPort("\\A", sig_a); + cell->setPort("\\Y", sig_y); + return cell; +} + +RTLIL::Cell* RTLIL::Module::addTribuf(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_y) +{ + RTLIL::Cell *cell = addCell(name, "$tribuf"); + cell->parameters["\\WIDTH"] = sig_a.size(); + cell->setPort("\\A", sig_a); + cell->setPort("\\EN", sig_en); + cell->setPort("\\Y", sig_y); return cell; } @@ -2012,7 +2144,7 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) return; } - if (type == "$lut") { + if (type == "$lut" || type == "$sop") { parameters["\\WIDTH"] = GetSize(connections_["\\A"]); return; } @@ -2453,8 +2585,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) @@ -2464,18 +2606,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) @@ -2539,8 +2689,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) @@ -2568,31 +2745,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(); +} - bits_.swap(new_bits); - width_ = GetSize(bits_); +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"); + + 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(); @@ -2600,8 +2789,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 @@ -2785,7 +3003,7 @@ void RTLIL::SigSpec::extend_u0(int width, bool is_signed) if (width_ > width) remove(width, width_ - width); - + if (width_ < width) { RTLIL::SigBit padding = width_ > 0 ? (*this)[width_ - 1] : RTLIL::State::S0; if (!is_signed) @@ -2939,6 +3157,21 @@ bool RTLIL::SigSpec::is_fully_const() const return true; } +bool RTLIL::SigSpec::is_fully_zero() const +{ + cover("kernel.rtlil.sigspec.is_fully_zero"); + + pack(); + for (auto it = chunks_.begin(); it != chunks_.end(); it++) { + if (it->width > 0 && it->wire != NULL) + return false; + for (size_t i = 0; i < it->data.size(); i++) + if (it->data[i] != RTLIL::State::S0) + return false; + } + return true; +} + bool RTLIL::SigSpec::is_fully_def() const { cover("kernel.rtlil.sigspec.is_fully_def"); @@ -3062,6 +3295,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"); @@ -3149,18 +3393,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; @@ -3180,6 +3412,10 @@ bool RTLIL::SigSpec::parse(RTLIL::SigSpec &sig, RTLIL::Module *module, std::stri { cover("kernel.rtlil.sigspec.parse"); + AST::current_filename = "input"; + AST::use_internal_line_num(); + AST::set_line_num(0); + std::vector<std::string> tokens; sigspec_parse_split(tokens, str, ','); @@ -3339,7 +3575,7 @@ RTLIL::SwitchRule *RTLIL::SwitchRule::clone() const for (auto &it : cases) new_switchrule->cases.push_back(it->clone()); return new_switchrule; - + } RTLIL::SyncRule *RTLIL::SyncRule::clone() const @@ -3371,7 +3607,7 @@ RTLIL::Process *RTLIL::Process::clone() const for (auto &it : syncs) new_proc->syncs.push_back(it->clone()); - + return new_proc; } diff --git a/kernel/rtlil.h b/kernel/rtlil.h index dd40e2fba..274f97023 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -2,11 +2,11 @@ * 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 @@ -53,6 +53,7 @@ namespace RTLIL }; struct Const; + struct AttrObject; struct Selection; struct Monitor; struct Design; @@ -191,14 +192,14 @@ 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 convinience functions for better compatibility with std::string. + // The methods below are just convenience functions for better compatibility with std::string. bool operator==(const std::string &rhs) const { return str() == rhs; } bool operator!=(const std::string &rhs) const { return str() != rhs; } @@ -208,7 +209,7 @@ namespace RTLIL char operator[](size_t i) const { const char *p = c_str(); - for (; i != 0; i--, p++) + for (; i != 0; i--, p++) log_assert(*p != 0); return *p; } @@ -459,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; @@ -474,6 +475,16 @@ struct RTLIL::Const std::string decode_string() 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 RTLIL::Const extract(int offset, int len = 1, RTLIL::State padding = RTLIL::State::S0) const { + RTLIL::Const ret; + ret.bits.reserve(len); + for (int i = offset; i < offset + len; i++) + ret.bits.push_back(i < GetSize(bits) ? bits[i] : padding); + return ret; + } inline unsigned int hash() const { unsigned int h = mkhash_init; @@ -483,6 +494,17 @@ struct RTLIL::Const } }; +struct RTLIL::AttrObject +{ + dict<RTLIL::IdString, RTLIL::Const> attributes; + + void set_bool_attribute(RTLIL::IdString id); + bool get_bool_attribute(RTLIL::IdString id) const; + void set_strpool_attribute(RTLIL::IdString id, const pool<string> &data); + void add_strpool_attribute(RTLIL::IdString id, const pool<string> &data); + pool<string> get_strpool_attribute(RTLIL::IdString id) const; +}; + struct RTLIL::SigChunk { RTLIL::Wire *wire; @@ -647,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(); @@ -668,8 +691,10 @@ 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; bool is_fully_def() const; bool is_fully_undef() const; bool has_const() const; @@ -681,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; @@ -689,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); @@ -767,6 +792,7 @@ struct RTLIL::Design int refcount_modules_; dict<RTLIL::IdString, RTLIL::Module*> modules_; + std::vector<AST::AstNode*> verilog_packages; std::vector<RTLIL::Selection> selection_stack; dict<RTLIL::IdString, RTLIL::Selection> selection_vars; @@ -777,6 +803,7 @@ struct RTLIL::Design RTLIL::ObjRange<RTLIL::Module*> modules(); RTLIL::Module *module(RTLIL::IdString name); + RTLIL::Module *top_module(); bool has(RTLIL::IdString id) const { return modules_.count(id) != 0; @@ -785,6 +812,7 @@ struct RTLIL::Design void add(RTLIL::Module *module); RTLIL::Module *addModule(RTLIL::IdString name); void remove(RTLIL::Module *module); + void rename(RTLIL::Module *module, RTLIL::IdString new_name); void scratchpad_unset(std::string varname); @@ -807,6 +835,14 @@ struct RTLIL::Design bool selected_module(RTLIL::Module *mod) const; bool selected_whole_module(RTLIL::Module *mod) const; + RTLIL::Selection &selection() { + return selection_stack.back(); + } + + const RTLIL::Selection &selection() const { + return selection_stack.back(); + } + bool full_selection() const { return selection_stack.back().full_selection; } @@ -831,18 +867,7 @@ struct RTLIL::Design std::vector<RTLIL::Module*> selected_whole_modules_warn() const; }; -#define RTLIL_ATTRIBUTE_MEMBERS \ - dict<RTLIL::IdString, RTLIL::Const> attributes; \ - void set_bool_attribute(RTLIL::IdString id) { \ - attributes[id] = RTLIL::Const(1); \ - } \ - bool get_bool_attribute(RTLIL::IdString id) const { \ - if (attributes.count(id) == 0) \ - return false; \ - return attributes.at(id).as_bool(); \ - } - -struct RTLIL::Module +struct RTLIL::Module : public RTLIL::AttrObject { unsigned int hashidx_; unsigned int hash() const { return hashidx_; } @@ -866,7 +891,6 @@ public: pool<RTLIL::IdString> avail_parameters; dict<RTLIL::IdString, RTLIL::Memory*> memories; dict<RTLIL::IdString, RTLIL::Process*> processes; - RTLIL_ATTRIBUTE_MEMBERS Module(); virtual ~Module(); @@ -933,25 +957,25 @@ public: RTLIL::Cell* addNot (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addPos (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addNeg (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false); - + RTLIL::Cell* addAnd (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addOr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addXor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addXnor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); - + RTLIL::Cell* addReduceAnd (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addReduceOr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addReduceXor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addReduceXnor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addReduceBool (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false); - + RTLIL::Cell* addShl (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addShr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addSshl (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addSshr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addShift (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addShiftx (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); - + RTLIL::Cell* addLt (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addLe (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addEq (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); @@ -960,24 +984,25 @@ public: RTLIL::Cell* addNex (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addGe (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addGt (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); - + RTLIL::Cell* addAdd (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addSub (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addMul (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addDiv (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addMod (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addPow (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool a_signed = false, bool b_signed = false); - + RTLIL::Cell* addLogicNot (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addLogicAnd (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); RTLIL::Cell* addLogicOr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false); - + RTLIL::Cell* addMux (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s, RTLIL::SigSpec sig_y); RTLIL::Cell* addPmux (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s, RTLIL::SigSpec sig_y); - + RTLIL::Cell* addSlice (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const offset); RTLIL::Cell* addConcat (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y); - RTLIL::Cell* addLut (RTLIL::IdString name, RTLIL::SigSpec sig_i, RTLIL::SigSpec sig_o, RTLIL::Const lut); + RTLIL::Cell* addLut (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const lut); + RTLIL::Cell* addTribuf (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_y); RTLIL::Cell* addAssert (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en); RTLIL::Cell* addEquiv (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y); @@ -992,6 +1017,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); @@ -1063,6 +1089,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); @@ -1077,7 +1104,7 @@ public: RTLIL::SigBit Oai4Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c, RTLIL::SigBit sig_d); }; -struct RTLIL::Wire +struct RTLIL::Wire : public RTLIL::AttrObject { unsigned int hashidx_; unsigned int hash() const { return hashidx_; } @@ -1097,10 +1124,9 @@ public: RTLIL::IdString name; int width, start_offset, port_id; bool port_input, port_output, upto; - RTLIL_ATTRIBUTE_MEMBERS }; -struct RTLIL::Memory +struct RTLIL::Memory : public RTLIL::AttrObject { unsigned int hashidx_; unsigned int hash() const { return hashidx_; } @@ -1109,10 +1135,9 @@ struct RTLIL::Memory RTLIL::IdString name; int width, start_offset, size; - RTLIL_ATTRIBUTE_MEMBERS }; -struct RTLIL::Cell +struct RTLIL::Cell : public RTLIL::AttrObject { unsigned int hashidx_; unsigned int hash() const { return hashidx_; } @@ -1132,7 +1157,6 @@ public: RTLIL::IdString type; dict<RTLIL::IdString, RTLIL::SigSpec> connections_; dict<RTLIL::IdString, RTLIL::Const> parameters; - RTLIL_ATTRIBUTE_MEMBERS // access cell ports bool hasPort(RTLIL::IdString portname) const; @@ -1177,10 +1201,9 @@ struct RTLIL::CaseRule RTLIL::CaseRule *clone() const; }; -struct RTLIL::SwitchRule +struct RTLIL::SwitchRule : public RTLIL::AttrObject { RTLIL::SigSpec signal; - RTLIL_ATTRIBUTE_MEMBERS std::vector<RTLIL::CaseRule*> cases; ~SwitchRule(); @@ -1199,10 +1222,9 @@ struct RTLIL::SyncRule RTLIL::SyncRule *clone() const; }; -struct RTLIL::Process +struct RTLIL::Process : public RTLIL::AttrObject { RTLIL::IdString name; - RTLIL_ATTRIBUTE_MEMBERS RTLIL::CaseRule root_case; std::vector<RTLIL::SyncRule*> syncs; diff --git a/kernel/satgen.h b/kernel/satgen.h index 2f5efe674..e118c1569 100644 --- a/kernel/satgen.h +++ b/kernel/satgen.h @@ -2,11 +2,11 @@ * 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 @@ -29,7 +29,37 @@ YOSYS_NAMESPACE_BEGIN -typedef ezMiniSAT ezDefaultSAT; +// defined in kernel/register.cc +extern struct SatSolver *yosys_satsolver_list; +extern struct SatSolver *yosys_satsolver; + +struct SatSolver +{ + string name; + SatSolver *next; + virtual ezSAT *create() = 0; + + SatSolver(string name) : name(name) { + next = yosys_satsolver_list; + yosys_satsolver_list = this; + } + + virtual ~SatSolver() { + auto p = &yosys_satsolver_list; + while (*p) { + if (*p == this) + *p = next; + else + p = &(*p)->next; + } + if (yosys_satsolver == this) + yosys_satsolver = yosys_satsolver_list; + } +}; + +struct ezSatPtr : public std::unique_ptr<ezSAT> { + ezSatPtr() : unique_ptr<ezSAT>(yosys_satsolver->create()) { } +}; struct SatGen { @@ -38,6 +68,7 @@ struct SatGen std::string prefix; SigPool initial_state; std::map<std::string, RTLIL::SigSpec> asserts_a, asserts_en; + std::map<std::string, RTLIL::SigSpec> assumes_a, assumes_en; std::map<std::string, std::map<RTLIL::SigBit, int>> imported_signals; bool ignore_div_by_zero; bool model_undef; @@ -131,6 +162,13 @@ struct SatGen sig_en = asserts_en[pf]; } + void getAssumes(RTLIL::SigSpec &sig_a, RTLIL::SigSpec &sig_en, int timestep = -1) + { + std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep)); + sig_a = assumes_a[pf]; + sig_en = assumes_en[pf]; + } + int importAsserts(int timestep = -1) { std::vector<int> check_bits, enable_bits; @@ -145,6 +183,20 @@ struct SatGen return ez->vec_reduce_and(ez->vec_or(check_bits, ez->vec_not(enable_bits))); } + int importAssumes(int timestep = -1) + { + std::vector<int> check_bits, enable_bits; + std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep)); + if (model_undef) { + check_bits = ez->vec_and(ez->vec_not(importUndefSigSpec(assumes_a[pf], timestep)), importDefSigSpec(assumes_a[pf], timestep)); + enable_bits = ez->vec_and(ez->vec_not(importUndefSigSpec(assumes_en[pf], timestep)), importDefSigSpec(assumes_en[pf], timestep)); + } else { + check_bits = importDefSigSpec(assumes_a[pf], timestep); + enable_bits = importDefSigSpec(assumes_en[pf], timestep); + } + return ez->vec_reduce_and(ez->vec_or(check_bits, ez->vec_not(enable_bits))); + } + int signals_eq(RTLIL::SigSpec lhs, RTLIL::SigSpec rhs, int timestep_lhs = -1, int timestep_rhs = -1) { if (timestep_rhs < 0) @@ -928,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()); @@ -996,6 +1048,88 @@ struct SatGen return true; } + if (cell->type == "$sop") + { + std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep); + int y = importDefSigSpec(cell->getPort("\\Y"), timestep).at(0); + + int width = cell->getParam("\\WIDTH").as_int(); + int depth = cell->getParam("\\DEPTH").as_int(); + + vector<State> table_raw = cell->getParam("\\TABLE").bits; + while (GetSize(table_raw) < 2*width*depth) + table_raw.push_back(State::S0); + + vector<vector<int>> table(depth); + + for (int i = 0; i < depth; i++) + for (int j = 0; j < width; j++) + { + bool pat0 = (table_raw[2*width*i + 2*j + 0] == State::S1); + bool pat1 = (table_raw[2*width*i + 2*j + 1] == State::S1); + + if (pat0 && !pat1) + table.at(i).push_back(0); + else if (!pat0 && pat1) + table.at(i).push_back(1); + else + table.at(i).push_back(-1); + } + + if (model_undef) + { + std::vector<int> products, undef_products; + std::vector<int> undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep); + int undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep).at(0); + + for (int i = 0; i < depth; i++) + { + std::vector<int> cmp_a, cmp_ua, cmp_b; + + for (int j = 0; j < width; j++) + if (table.at(i).at(j) >= 0) { + cmp_a.push_back(a.at(j)); + cmp_ua.push_back(undef_a.at(j)); + cmp_b.push_back(table.at(i).at(j) ? ez->CONST_TRUE : ez->CONST_FALSE); + } + + std::vector<int> masked_a = ez->vec_or(cmp_a, cmp_ua); + std::vector<int> masked_b = ez->vec_or(cmp_b, cmp_ua); + + int masked_eq = ez->vec_eq(masked_a, masked_b); + int any_undef = ez->expression(ezSAT::OpOr, cmp_ua); + + undef_products.push_back(ez->AND(any_undef, masked_eq)); + products.push_back(ez->AND(ez->NOT(any_undef), masked_eq)); + } + + int yy = ez->expression(ezSAT::OpOr, products); + ez->SET(undef_y, ez->AND(ez->NOT(yy), ez->expression(ezSAT::OpOr, undef_products))); + undefGating(y, yy, undef_y); + } + else + { + std::vector<int> products; + + for (int i = 0; i < depth; i++) + { + std::vector<int> cmp_a, cmp_b; + + for (int j = 0; j < width; j++) + if (table.at(i).at(j) >= 0) { + cmp_a.push_back(a.at(j)); + cmp_b.push_back(table.at(i).at(j) ? ez->CONST_TRUE : ez->CONST_FALSE); + } + + products.push_back(ez->vec_eq(cmp_a, cmp_b)); + } + + ez->SET(y, ez->expression(ezSAT::OpOr, products)); + } + + return true; + } + if (cell->type == "$fa") { std::vector<int> a = importDefSigSpec(cell->getPort("\\A"), timestep); @@ -1204,6 +1338,14 @@ struct SatGen return true; } + if (cell->type == "$assume") + { + std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep)); + assumes_a[pf].append((*sigmap)(cell->getPort("\\A"))); + assumes_en[pf].append((*sigmap)(cell->getPort("\\EN"))); + return true; + } + // Unsupported internal cell types: $pow $lut // .. and all sequential cells except $dff and $_DFF_[NP]_ return false; diff --git a/kernel/sigtools.h b/kernel/sigtools.h index f92a87dbb..4e97bb775 100644 --- a/kernel/sigtools.h +++ b/kernel/sigtools.h @@ -2,11 +2,11 @@ * 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 @@ -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]); - if (bf.wire == NULL) - continue; + const RTLIL::SigBit &bf = database[bfi]; + const RTLIL::SigBit &bt = database[bti]; - register_bit(bf); - register_bit(bt); + if (bf.wire || bt.wire) + { + database.imerge(bfi, bti); - if (bt.wire != NULL) - merge_bit(bf, bt); - else - set_bit(bf, bt); + if (bf.wire == nullptr) + database.ipromote(bfi); + + 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,10 +312,19 @@ struct SigMap RTLIL::SigSpec operator()(RTLIL::Wire *wire) const { - RTLIL::SigSpec sig(wire); + SigSpec sig(wire); apply(sig); return sig; } + + RTLIL::SigSpec allbits() const + { + RTLIL::SigSpec sig; + for (auto &bit : database) + if (bit.wire != nullptr) + sig.append(bit); + return sig; + } }; YOSYS_NAMESPACE_END diff --git a/kernel/utils.h b/kernel/utils.h index 2ec6182ea..8942905fe 100644 --- a/kernel/utils.h +++ b/kernel/utils.h @@ -2,11 +2,11 @@ * 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 @@ -186,7 +186,7 @@ struct TopoSort active_stack.pop_back(); active_cells.erase(n); } - + marked_cells.insert(n); sorted.push_back(n); } diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 530d78796..8da57fd4e 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -2,11 +2,11 @@ * 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 @@ -104,7 +104,7 @@ void yosys_banner() log(" | |\n"); log(" | yosys -- Yosys Open SYnthesis Suite |\n"); log(" | |\n"); - log(" | Copyright (C) 2012 - 2015 Clifford Wolf <clifford@clifford.at> |\n"); + log(" | Copyright (C) 2012 - 2016 Clifford Wolf <clifford@clifford.at> |\n"); log(" | |\n"); log(" | Permission to use, copy, modify, and/or distribute this software for any |\n"); log(" | purpose with or without fee is hereby granted, provided that the above |\n"); @@ -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) { @@ -182,13 +194,23 @@ int readsome(std::istream &f, char *s, int n) return rc; } -std::string next_token(std::string &text, const char *sep) +std::string next_token(std::string &text, const char *sep, bool long_strings) { size_t pos_begin = text.find_first_not_of(sep); if (pos_begin == std::string::npos) pos_begin = text.size(); + if (long_strings && pos_begin != text.size() && text[pos_begin] == '"') { + string sep_string = sep; + for (size_t i = pos_begin+1; i < text.size(); i++) + if (text[i] == '"' && (i+1 == text.size() || sep_string.find(text[i+1]) != std::string::npos)) { + std::string token = text.substr(pos_begin, i-pos_begin+1); + text = text.substr(i+1); + return token; + } + } + size_t pos_end = text.find_first_of(sep, pos_begin); if (pos_end == std::string::npos) @@ -199,6 +221,26 @@ std::string next_token(std::string &text, const char *sep) return token; } +std::vector<std::string> split_tokens(const std::string &text, const char *sep) +{ + std::vector<std::string> tokens; + std::string current_token; + for (char c : text) { + if (strchr(sep, c)) { + if (!current_token.empty()) { + tokens.push_back(current_token); + current_token.clear(); + } + } else + current_token += c; + } + if (!current_token.empty()) { + tokens.push_back(current_token); + current_token.clear(); + } + return tokens; +} + // this is very similar to fnmatch(). the exact rules used by this // function are: // @@ -376,6 +418,15 @@ bool check_file_exists(std::string filename, bool is_exec) } #endif +bool is_absolute_path(std::string filename) +{ +#ifdef _WIN32 + return filename[0] == '/' || filename[0] == '\\' || (filename[0] != 0 && filename[1] == ':'); +#else + return filename[0] == '/'; +#endif +} + void remove_directory(std::string dirname) { #ifdef _WIN32 @@ -496,6 +547,14 @@ const char *create_prompt(RTLIL::Design *design, int recursion_counter) return buffer; } +void rewrite_filename(std::string &filename) +{ + if (filename.substr(0, 1) == "\"" && filename.substr(GetSize(filename)-1) == "\"") + filename = filename.substr(1, GetSize(filename)-2); + if (filename.substr(0, 2) == "+/") + filename = proc_share_dirname() + filename.substr(2); +} + #ifdef YOSYS_ENABLE_TCL static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) { @@ -549,7 +608,7 @@ struct TclPass : public Pass { log("The tcl command 'yosys -import' can be used to import all yosys\n"); log("commands directly as tcl commands to the tcl shell. The yosys\n"); log("command 'proc' is wrapped using the tcl command 'procs' in order\n"); - log("to avoid a name collision with the tcl builting command 'proc'.\n"); + log("to avoid a name collision with the tcl builtin command 'proc'.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { @@ -619,26 +678,38 @@ std::string proc_self_dirname() #error Dont know how to determine process executable base path! #endif +#ifdef EMSCRIPTEN +std::string proc_share_dirname() +{ + return "/share"; +} +#else std::string proc_share_dirname() { std::string proc_self_path = proc_self_dirname(); -#ifdef _WIN32 +# ifdef _WIN32 std::string proc_share_path = proc_self_path + "share\\"; if (check_file_exists(proc_share_path, true)) return proc_share_path; proc_share_path = proc_self_path + "..\\share\\"; if (check_file_exists(proc_share_path, true)) return proc_share_path; -#else +# else std::string proc_share_path = proc_self_path + "share/"; if (check_file_exists(proc_share_path, true)) return proc_share_path; proc_share_path = proc_self_path + "../share/yosys/"; if (check_file_exists(proc_share_path, true)) return proc_share_path; -#endif +# ifdef YOSYS_DATDIR + proc_share_path = YOSYS_DATDIR "/"; + if (check_file_exists(proc_share_path, true)) + return proc_share_path; +# endif +# endif log_error("proc_share_dirname: unable to determine share/ directory!\n"); } +#endif bool fgetline(FILE *f, std::string &buffer) { @@ -664,6 +735,9 @@ static void handle_label(std::string &command, bool &from_to_active, const std:: while (pos < GetSize(command) && (command[pos] == ' ' || command[pos] == '\t')) pos++; + if (pos < GetSize(command) && command[pos] == '#') + return; + while (pos < GetSize(command) && command[pos] != ' ' && command[pos] != '\t' && command[pos] != '\r' && command[pos] != '\n') label += command[pos++]; @@ -689,6 +763,10 @@ void run_frontend(std::string filename, std::string command, std::string *backen command = "verilog"; else if (filename.size() > 2 && filename.substr(filename.size()-3) == ".sv") command = "verilog -sv"; + else if (filename.size() > 2 && filename.substr(filename.size()-4) == ".vhd") + command = "vhdl"; + else if (filename.size() > 4 && filename.substr(filename.size()-5) == ".blif") + command = "blif"; else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") command = "ilang"; else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".ys") @@ -802,6 +880,10 @@ void run_backend(std::string filename, std::string command, RTLIL::Design *desig command = "ilang"; else if (filename.size() > 5 && filename.substr(filename.size()-5) == ".blif") command = "blif"; + else if (filename.size() > 5 && filename.substr(filename.size()-5) == ".edif") + command = "edif"; + else if (filename.size() > 5 && filename.substr(filename.size()-5) == ".json") + command = "json"; else if (filename == "-") command = "ilang"; else if (filename.empty()) @@ -1018,8 +1100,8 @@ struct HistoryPass : public Pass { } HistoryPass; #endif -struct ScriptPass : public Pass { - ScriptPass() : Pass("script", "execute commands from script file") { } +struct ScriptCmdPass : public Pass { + ScriptCmdPass() : Pass("script", "execute commands from script file") { } virtual void help() { log("\n"); log(" script <filename> [<from_label>:<to_label>]\n"); @@ -1045,7 +1127,7 @@ struct ScriptPass : public Pass { else extra_args(args, 2, design, false); } -} ScriptPass; +} ScriptCmdPass; YOSYS_NAMESPACE_END diff --git a/kernel/yosys.h b/kernel/yosys.h index 47275ecd4..0df750a13 100644 --- a/kernel/yosys.h +++ b/kernel/yosys.h @@ -2,11 +2,11 @@ * 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 @@ -41,6 +41,7 @@ #include <map> #include <set> +#include <tuple> #include <vector> #include <string> #include <algorithm> @@ -49,6 +50,8 @@ #include <unordered_set> #include <initializer_list> #include <stdexcept> +#include <memory> +#include <cmath> #include <sstream> #include <fstream> @@ -89,9 +92,9 @@ # define mkdir _mkdir # define popen _popen # define pclose _pclose -# define PATH_MAX MAX_PATH # ifndef __MINGW32__ +# define PATH_MAX MAX_PATH # define isatty _isatty # define fileno _fileno # endif @@ -137,8 +140,27 @@ 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 { + std::shared_ptr<string> content; + shared_str() { } + shared_str(string s) { content = std::shared_ptr<string>(new string(s)); } + shared_str(const char *s) { content = std::shared_ptr<string>(new string(s)); } + const char *c_str() const { return content->c_str(); } + const string &str() const { return *content; } + bool operator==(const shared_str &other) const { return *content == *other.content; } + unsigned int hash() const { return hashlib::hash_ops<std::string>::hash(*content); } +}; + using hashlib::mkhash; using hashlib::mkhash_init; using hashlib::mkhash_add; @@ -150,6 +172,7 @@ using hashlib::hash_obj_ops; using hashlib::dict; using hashlib::idict; using hashlib::pool; +using hashlib::mfp; namespace RTLIL { struct IdString; @@ -200,15 +223,18 @@ 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); -std::string next_token(std::string &text, const char *sep = " \t\r\n"); +std::string next_token(std::string &text, const char *sep = " \t\r\n", bool long_strings = false); +std::vector<std::string> split_tokens(const std::string &text, const char *sep = " \t\r\n"); bool patmatch(const char *pattern, const char *string); int run_command(const std::string &command, std::function<void(const std::string&)> process_line = std::function<void(const std::string&)>()); std::string make_temp_file(std::string template_str = "/tmp/yosys_XXXXXX"); std::string make_temp_dir(std::string template_str = "/tmp/yosys_XXXXXX"); bool check_file_exists(std::string filename, bool is_exec = false); +bool is_absolute_path(std::string filename); void remove_directory(std::string dirname); template<typename T> int GetSize(const T &obj) { return obj.size(); } @@ -226,6 +252,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> {}; @@ -252,6 +280,7 @@ RTLIL::Design *yosys_get_design(); std::string proc_self_dirname(); std::string proc_share_dirname(); const char *create_prompt(RTLIL::Design *design, int recursion_counter); +void rewrite_filename(std::string &filename); void run_pass(std::string command, RTLIL::Design *design = nullptr); void run_frontend(std::string filename, std::string command, std::string *backend_command, std::string *from_to_label = nullptr, RTLIL::Design *design = nullptr); diff --git a/libs/ezsat/demo_bit.cc b/libs/ezsat/demo_bit.cc index 2a5099bf7..c7b11246c 100644 --- a/libs/ezsat/demo_bit.cc +++ b/libs/ezsat/demo_bit.cc @@ -2,11 +2,11 @@ * ezSAT -- A simple and easy to use CNF generator for SAT solvers * * Copyright (C) 2013 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 diff --git a/libs/ezsat/demo_cmp.cc b/libs/ezsat/demo_cmp.cc index b2df8a8d9..8d7ceb2b4 100644 --- a/libs/ezsat/demo_cmp.cc +++ b/libs/ezsat/demo_cmp.cc @@ -2,11 +2,11 @@ * ezSAT -- A simple and easy to use CNF generator for SAT solvers * * Copyright (C) 2013 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 diff --git a/libs/ezsat/demo_vec.cc b/libs/ezsat/demo_vec.cc index b994f00d6..eb8d75997 100644 --- a/libs/ezsat/demo_vec.cc +++ b/libs/ezsat/demo_vec.cc @@ -2,11 +2,11 @@ * ezSAT -- A simple and easy to use CNF generator for SAT solvers * * Copyright (C) 2013 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 diff --git a/libs/ezsat/ezminisat.cc b/libs/ezsat/ezminisat.cc index dee82a8df..e0ee6292d 100644 --- a/libs/ezsat/ezminisat.cc +++ b/libs/ezsat/ezminisat.cc @@ -2,11 +2,11 @@ * ezSAT -- A simple and easy to use CNF generator for SAT solvers * * Copyright (C) 2013 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 diff --git a/libs/ezsat/ezminisat.h b/libs/ezsat/ezminisat.h index 5b5252d88..983e6fd0e 100644 --- a/libs/ezsat/ezminisat.h +++ b/libs/ezsat/ezminisat.h @@ -2,11 +2,11 @@ * ezSAT -- A simple and easy to use CNF generator for SAT solvers * * Copyright (C) 2013 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 diff --git a/libs/ezsat/ezsat.cc b/libs/ezsat/ezsat.cc index 8d232f335..177bcd8a3 100644 --- a/libs/ezsat/ezsat.cc +++ b/libs/ezsat/ezsat.cc @@ -2,11 +2,11 @@ * ezSAT -- A simple and easy to use CNF generator for SAT solvers * * Copyright (C) 2013 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 @@ -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()); @@ -1373,7 +1395,7 @@ int ezSAT::manyhot(const std::vector<int> &vec, int min_hot, int max_hot) if (max_hot < 0) max_hot = min_hot; - + std::vector<int> formula; int M = max_hot+1, N = vec.size(); std::map<std::pair<int,int>, int> x; diff --git a/libs/ezsat/ezsat.h b/libs/ezsat/ezsat.h index 0faaa6b8d..85b13685f 100644 --- a/libs/ezsat/ezsat.h +++ b/libs/ezsat/ezsat.h @@ -2,11 +2,11 @@ * ezSAT -- A simple and easy to use CNF generator for SAT solvers * * Copyright (C) 2013 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 @@ -29,7 +29,7 @@ class ezSAT { - // each token (terminal or non-terminal) is represented by an interger number + // each token (terminal or non-terminal) is represented by an integer number // // the zero token: // the number zero is not used as valid token number and is used to encode diff --git a/libs/ezsat/puzzle3d.cc b/libs/ezsat/puzzle3d.cc index aee0044b4..59f840f9e 100644 --- a/libs/ezsat/puzzle3d.cc +++ b/libs/ezsat/puzzle3d.cc @@ -2,11 +2,11 @@ * ezSAT -- A simple and easy to use CNF generator for SAT solvers * * Copyright (C) 2013 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 @@ -253,7 +253,7 @@ int main() } for (size_t i = 1; i < vecvec.size(); i++) ez.assume(ez.ordered(vecvec[0], vecvec[1])); - + printf("Found and eliminated %d spatial symmetries.\n", int(symmetries.size())); printf("Generated %d clauses over %d variables.\n", ez.numCnfClauses(), ez.numCnfVariables()); diff --git a/libs/ezsat/testbench.cc b/libs/ezsat/testbench.cc index d20258c37..d6dc41fa9 100644 --- a/libs/ezsat/testbench.cc +++ b/libs/ezsat/testbench.cc @@ -2,11 +2,11 @@ * ezSAT -- A simple and easy to use CNF generator for SAT solvers * * Copyright (C) 2013 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 @@ -220,12 +220,12 @@ void test_count(uint32_t x) fprintf(stderr, "FAILED 6bit-no-clipping test!\n"); abort(); } - + if (cv4 != sat.vec_count(v, 4, true)) { fprintf(stderr, "FAILED 4bit-clipping test!\n"); abort(); } - + printf("ok.\n"); } diff --git a/libs/subcircuit/README b/libs/subcircuit/README index 757a9f540..b1335681e 100644 --- a/libs/subcircuit/README +++ b/libs/subcircuit/README @@ -330,7 +330,7 @@ Mining for frequent SubCircuits The solver also contains a miner for frequent subcircuits. The following code fragment will find all frequent subcircuits with at least minNodes nodes and -at most maxNodes nodes that occurs at least minMatches times: +at most maxNodes nodes that occurs at least minMatches times: std::vector<SubCircuit::Solver::MineResult> results; mySolver.mine(results, minNodes, maxNodes, minMatches); @@ -370,7 +370,7 @@ This package also contains a small command-line tool called "scshell" that can be used for experimentation with the algorithm. This program reads a series of commands from stdin and reports its findings to stdout on exit. - $ ./scshell < test_macc22.txt + $ ./scshell < test_macc22.txt ... diff --git a/libs/subcircuit/subcircuit.cc b/libs/subcircuit/subcircuit.cc index cf14df0ac..7c7236833 100644 --- a/libs/subcircuit/subcircuit.cc +++ b/libs/subcircuit/subcircuit.cc @@ -3,11 +3,11 @@ * algorithm for coarse grain logic networks * * Copyright (C) 2013 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 diff --git a/libs/subcircuit/subcircuit.h b/libs/subcircuit/subcircuit.h index d673af88d..5291c6421 100644 --- a/libs/subcircuit/subcircuit.h +++ b/libs/subcircuit/subcircuit.h @@ -3,11 +3,11 @@ * algorithm for coarse grain logic networks * * Copyright (C) 2013 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 @@ -115,7 +115,7 @@ namespace SubCircuit private: SolverWorker *worker; - + protected: virtual bool userCompareNodes(const std::string &needleGraphId, const std::string &needleNodeId, void *needleUserData, const std::string &haystackGraphId, const std::string &haystackNodeId, void *haystackUserData, const std::map<std::string, std::string> &portMapping); diff --git a/libs/subcircuit/test_large.spl b/libs/subcircuit/test_large.spl index 74a47d94f..e33e26985 100644 --- a/libs/subcircuit/test_large.spl +++ b/libs/subcircuit/test_large.spl @@ -99,7 +99,7 @@ function makeGraph(seed, gates, primaryInputs, primaryOutputs) foreach netDecl (unusedOutpus) push primaryOutputs, netDecl; - + return code; } diff --git a/manual/APPNOTE_010_Verilog_to_BLIF.tex b/manual/APPNOTE_010_Verilog_to_BLIF.tex index 9ee87bc44..0ecdf6194 100644 --- a/manual/APPNOTE_010_Verilog_to_BLIF.tex +++ b/manual/APPNOTE_010_Verilog_to_BLIF.tex @@ -61,7 +61,7 @@ to easily create complex designs from small HDL code. It is the preferred method of design entry for many designers\footnote{The other half prefers VHDL, a very different but -- of course -- equally powerful language.}. -The Berkeley Logic Interchange Format (BLIF) is a simple file format for +The Berkeley Logic Interchange Format (BLIF) \cite{blif} is a simple file format for exchanging sequential logic between programs. It is easy to generate and easy to parse and is therefore the preferred method of design entry for many authors of logic synthesis tools. @@ -100,7 +100,7 @@ regression testing Yosys. \section{Getting Started} -We start our tour with the Navré processor from yosys-bigsim. The Navré +We start our tour with the Navr\'e processor from yosys-bigsim. The Navr\'e processor \cite{navre} is an Open Source AVR clone. It is a single module ({\tt softusb\_navre}) in a single design file ({\tt softusb\_navre.v}). It also is using only features that map nicely to the BLIF format, for example it only @@ -150,11 +150,11 @@ write_blif softusb_navre.blif \end{figure} The first and last line obviously read the Verilog file and write the BLIF -file. +file. \medskip -The 2nd line checks the design hierarchy and instantiates parametrized +The 2nd line checks the design hierarchy and instantiates parametrized versions of the modules in the design, if necessary. In the case of this simple design this is a no-op. However, as a general rule a synthesis script should always contain this command as first command after reading the input @@ -174,7 +174,7 @@ instead of {\tt opt}. \item The command {\tt proc} converts {\it processes} (Yosys' internal representation of Verilog {\tt always}- and {\tt initial}-blocks) to circuits of multiplexers and storage elements (various types of flip-flops). -\item The command {\tt memory} converts Yosys' internal representations of +\item The command {\tt memory} converts Yosys' internal representations of arrays and array accesses to multi-port block memories, and then maps this block memories to address decoders and flip-flops, unless the option {\tt -nomap} is used, in which case the multi-port block memories stay in the design @@ -226,7 +226,7 @@ further processed using custom commands. But in this case we don't want that. \medskip So now we have the final synthesis script for generating a BLIF file -for the Navré CPU: +for the Navr\'e CPU: \begin{figure}[H] \begin{lstlisting}[language=sh] @@ -445,7 +445,7 @@ yosys-bigsim, a collection of real-world Verilog designs for regression testing \url{https://github.com/cliffordwolf/yosys-bigsim} \bibitem{navre} -Sebastien Bourdeauducq. Navré AVR clone (8-bit RISC). \\ +Sebastien Bourdeauducq. Navr\'e AVR clone (8-bit RISC). \\ \url{http://opencores.org/project,navre} \bibitem{amber} @@ -456,6 +456,10 @@ Conor Santifort. Amber ARM-compatible core. \\ Berkeley Logic Synthesis and Verification Group. ABC: A System for Sequential Synthesis and Verification. \\ \url{http://www.eecs.berkeley.edu/~alanmi/abc/} +\bibitem{blif} +Berkeley Logic Interchange Format (BLIF) \\ +\url{http://vlsi.colorado.edu/~vis/blif.ps} + \end{thebibliography} diff --git a/manual/APPNOTE_011_Design_Investigation.tex b/manual/APPNOTE_011_Design_Investigation.tex index 504ab7ec6..9780c7833 100644 --- a/manual/APPNOTE_011_Design_Investigation.tex +++ b/manual/APPNOTE_011_Design_Investigation.tex @@ -54,7 +54,7 @@ \begin{document} \title{Yosys Application Note 011: \\ Interactive Design Investigation} -\author{Clifford Wolf \\ Original Verision December 2013} +\author{Clifford Wolf \\ Original Version December 2013} \maketitle \begin{abstract} @@ -256,7 +256,7 @@ Verilog file containing blackbox modules. There are two ways to load cell descriptions into Yosys: First the Verilog file for the cell library can be passed directly to the {\tt show} command using the {\tt -lib <filename>} option. Secondly it is possible to load cell libraries into the design with -the {\tt read\_verilog -lib <filename>} command. The 2nd method has the great +the {\tt read\_verilog -lib <filename>} command. The 2nd method has the great advantage that the library only needs to be loaded once and can then be used in all subsequent calls to the {\tt show} command. @@ -296,7 +296,7 @@ In addition to {\it what\/} to display one also needs to carefully decide {\it when\/} to display it, with respect to the synthesis flow. In general it is a good idea to troubleshoot a circuit in the earliest state in which a problem can be reproduced. So if, for example, the internal state before calling -the {\tt techmap} command already fails to verify, it is better to troubleshoot +the {\tt techmap} command already fails to verify, it is better to troubleshoot the coarse-grain version of the circuit before {\tt techmap} than the gate-level circuit after {\tt techmap}. @@ -316,7 +316,7 @@ yosys> ls 1 modules: example -yosys> cd example +yosys> cd example yosys [example]> ls @@ -497,7 +497,7 @@ using them will get its own net label. In this case however we would like to see the cells connected properly. This can be achieved using the {\tt \%x} action, that broadens the selection, i.e. for each selected wire it selects all cells connected to the wire and vice -versa. So {\tt show a:sumstuff \%x} yields the diagram schon in Fig.~\ref{sumprod_01}. +versa. So {\tt show a:sumstuff \%x} yields the diagram shown in Fig.~\ref{sumprod_01}. \begin{figure}[t] \includegraphics[width=\linewidth]{APPNOTE_011_Design_Investigation/sumprod_01.pdf} @@ -708,7 +708,7 @@ For example (see Fig.~\ref{submod} for the circuit diagram of {\tt selstage}): {\scriptsize \begin{verbatim} yosys [selstage]> eval -set s2,s1 4'b1001 -set d 4'hc -show n2 -show n1 - + 9. Executing EVAL pass (evaluate the circuit given an input). Full command line: eval -set s2,s1 4'b1001 -set d 4'hc -show n2 -show n1 Eval result: \n2 = 2'10. @@ -729,10 +729,10 @@ The {\tt -table} option can be used to create a truth table. For example: {\scriptsize \begin{verbatim} yosys [selstage]> eval -set-undef -set d[3:1] 0 -table s1,d[0] - + 10. Executing EVAL pass (evaluate the circuit given an input). Full command line: eval -set-undef -set d[3:1] 0 -table s1,d[0] - + \s1 \d [0] | \n1 \n2 ---- ------ | ---- ---- 2'00 1'0 | 2'00 2'00 @@ -743,8 +743,8 @@ The {\tt -table} option can be used to create a truth table. For example: 2'10 1'1 | 2'xx 2'10 2'11 1'0 | 2'00 2'00 2'11 1'1 | 2'xx 2'11 - - Assumend undef (x) value for the following singals: \s2 + + Assumed undef (x) value for the following signals: \s2 \end{verbatim} } @@ -780,11 +780,11 @@ Final proof equation: \ok = 1'1 Solving problem with 2790 variables and 8241 clauses.. SAT proof finished - model found: FAIL! - ______ ___ ___ _ _ _ _ + ______ ___ ___ _ _ _ _ (_____ \ / __) / __) (_) | | | | _____) )___ ___ ___ _| |__ _| |__ _____ _| | _____ __| | | | ____/ ___) _ \ / _ (_ __) (_ __|____ | | || ___ |/ _ |_| - | | | | | |_| | |_| || | | | / ___ | | || ____( (_| |_ + | | | | | |_| | |_| || | | | / ___ | | || ____( (_| |_ |_| |_| \___/ \___/ |_| |_| \_____|_|\_)_____)\____|_| @@ -811,15 +811,15 @@ Final proof equation: \ok = 1'1 Solving problem with 2790 variables and 8257 clauses.. SAT proof finished - no model found: SUCCESS! - /$$$$$$ /$$$$$$$$ /$$$$$$$ - /$$__ $$ | $$_____/ | $$__ $$ - | $$ \ $$ | $$ | $$ \ $$ - | $$ | $$ | $$$$$ | $$ | $$ - | $$ | $$ | $$__/ | $$ | $$ - | $$/$$ $$ | $$ | $$ | $$ + /$$$$$$ /$$$$$$$$ /$$$$$$$ + /$$__ $$ | $$_____/ | $$__ $$ + | $$ \ $$ | $$ | $$ \ $$ + | $$ | $$ | $$$$$ | $$ | $$ + | $$ | $$ | $$__/ | $$ | $$ + | $$/$$ $$ | $$ | $$ | $$ | $$$$$$/ /$$| $$$$$$$$ /$$| $$$$$$$//$$ \____ $$$|__/|________/|__/|_______/|__/ - \__/ + \__/ \end{lstlisting} \caption{Experiments with the miter circuit from Fig.~\ref{primetest}. The first attempt of proving that 31 is prime failed because the SAT solver found a creative way of factorizing 31 using integer overflow.} @@ -840,20 +840,20 @@ corresponding input values. For Example: {\scriptsize \begin{verbatim} yosys [selstage]> sat -show s1,s2,d -set s1 s2 -set n2,n1 4'b1001 - + 11. Executing SAT pass (solving SAT problems in the circuit). Full command line: sat -show s1,s2,d -set s1 s2 -set n2,n1 4'b1001 - + Setting up SAT problem: Import set-constraint: \s1 = \s2 Import set-constraint: { \n2 \n1 } = 4'1001 Final constraint equation: { \n2 \n1 \s1 } = { 4'1001 \s2 } Imported 3 cells to SAT database. Import show expression: { \s1 \s2 \d } - + Solving problem with 81 variables and 207 clauses.. SAT solving finished - model found: - + Signal Name Dec Hex Bin -------------------- ---------- ---------- --------------- \d 9 9 1001 @@ -1061,7 +1061,7 @@ MiniSat: a minimalistic open-source SAT solver. \url{http://minisat.se/} \bibitem{tip} -Niklas Een and Niklas Sörensson (2003). +Niklas Een and Niklas S\"orensson (2003). Temporal Induction by Incremental SAT Solving. \url{http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.8161} diff --git a/manual/APPNOTE_012_Verilog_to_BTOR.tex b/manual/APPNOTE_012_Verilog_to_BTOR.tex new file mode 100644 index 000000000..1bc277876 --- /dev/null +++ b/manual/APPNOTE_012_Verilog_to_BTOR.tex @@ -0,0 +1,435 @@ + +% IEEEtran howto: +% http://ftp.univie.ac.at/packages/tex/macros/latex/contrib/IEEEtran/IEEEtran_HOWTO.pdf +\documentclass[9pt,technote,a4paper]{IEEEtran} + +\usepackage[T1]{fontenc} % required for luximono! +\usepackage[scaled=0.8]{luximono} % typewriter font with bold face + +% To install the luximono font files: +% getnonfreefonts-sys --all or +% getnonfreefonts-sys luximono +% +% when there are trouble you might need to: +% - Create /etc/texmf/updmap.d/99local-luximono.cfg +% containing the single line: Map ul9.map +% - Run update-updmap followed by mktexlsr and updmap-sys +% +% This commands must be executed as root with a root environment +% (i.e. run "sudo su" and then execute the commands in the root +% shell, don't just prefix the commands with "sudo"). + +\usepackage[unicode,bookmarks=false]{hyperref} +\usepackage[english]{babel} +\usepackage[utf8]{inputenc} +\usepackage{amssymb} +\usepackage{amsmath} +\usepackage{amsfonts} +\usepackage{units} +\usepackage{nicefrac} +\usepackage{eurosym} +\usepackage{graphicx} +\usepackage{verbatim} +\usepackage{algpseudocode} +\usepackage{scalefnt} +\usepackage{xspace} +\usepackage{color} +\usepackage{colortbl} +\usepackage{multirow} +\usepackage{hhline} +\usepackage{listings} +\usepackage{float} + +\usepackage{tikz} +\usetikzlibrary{calc} +\usetikzlibrary{arrows} +\usetikzlibrary{scopes} +\usetikzlibrary{through} +\usetikzlibrary{shapes.geometric} + +\lstset{basicstyle=\ttfamily,frame=trBL,xleftmargin=2em,xrightmargin=1em,numbers=left} + +\begin{document} + +\title{Yosys Application Note 012: \\ Converting Verilog to BTOR} +\author{Ahmed Irfan and Clifford Wolf \\ April 2015} +\maketitle + +\begin{abstract} +Verilog-2005 is a powerful Hardware Description Language (HDL) that +can be used to easily create complex designs from small HDL code. +BTOR~\cite{btor} is a bit-precise word-level format for model +checking. It is a simple format and easy to parse. It allows to model +the model checking problem over the theory of bit-vectors with +one-dimensional arrays, thus enabling to model Verilog designs with +registers and memories. Yosys~\cite{yosys} is an Open-Source Verilog +synthesis tool that can be used to convert Verilog designs with simple +assertions to BTOR format. + +\end{abstract} + +\section{Installation} + +Yosys written in C++ (using features from C++11) and is tested on +modern Linux. It should compile fine on most UNIX systems with a +C++11 compiler. The README file contains useful information on +building Yosys and its prerequisites. + +Yosys is a large and feature-rich program with some dependencies. For +this work, we may deactivate other extra features such as {\tt TCL} +and {\tt ABC} support in the {\tt Makefile}. + +\bigskip + +This Application Note is based on GIT Rev. {\tt 082550f} from +2015-04-04 of Yosys~\cite{yosys}. + +\section{Quick Start} + +We assume that the Verilog design is synthesizable and we also assume +that the design does not have multi-dimensional memories. As BTOR +implicitly initializes registers to zero value and memories stay +uninitialized, we assume that the Verilog design does +not contain initial blocks. For more details about the BTOR format, +please refer to~\cite{btor}. + +We provide a shell script {\tt verilog2btor.sh} which can be used to +convert a Verilog design to BTOR. The script can be found in the +{\tt backends/btor} directory. The following example shows its usage: + +\begin{figure}[H] +\begin{lstlisting}[language=sh,numbers=none] +verilog2btor.sh fsm.v fsm.btor test +\end{lstlisting} + \renewcommand{\figurename}{Listing} +\caption{Using verilog2btor script} +\end{figure} + +The script {\tt verilog2btor.sh} takes three parameters. In the above +example, the first parameter {\tt fsm.v} is the input design, the second +parameter {\tt fsm.btor} is the file name of BTOR output, and the third +parameter {\tt test} is the name of top module in the design. + +To specify the properties (that need to be checked), we have two +options: +\begin{itemize} +\item We can use the Verilog {\tt assert} statement in the procedural block + or module body of the Verilog design, as shown in + Listing~\ref{specifying_property_assert}. This is the preferred option. +\item We can use a single-bit output wire, whose name starts with + {\tt safety}. The value of this output wire needs to be driven low + when the property is met, i.e. the solver will try to find a model + that makes the safety pin go high. This is demonstrated in + Listing~\ref{specifying_property_output}. +\end{itemize} + +\begin{figure}[H] +\begin{lstlisting}[language=Verilog,numbers=none] +module test(input clk, input rst, output y); + + reg [2:0] state; + + always @(posedge clk) begin + if (rst || state == 3) begin + state <= 0; + end else begin + assert(state < 3); + state <= state + 1; + end + end + + assign y = state[2]; + + assert property (y !== 1'b1); + +endmodule +\end{lstlisting} +\renewcommand{\figurename}{Listing} +\caption{Specifying property in Verilog design with {\tt assert}} +\label{specifying_property_assert} +\end{figure} + +\begin{figure}[H] +\begin{lstlisting}[language=Verilog,numbers=none] +module test(input clk, input rst, + output y, output safety1); + + reg [2:0] state; + + always @(posedge clk) begin + if (rst || state == 3) + state <= 0; + else + state <= state + 1; + end + + assign y = state[2]; + + assign safety1 = !(y !== 1'b1); + +endmodule +\end{lstlisting} +\renewcommand{\figurename}{Listing} +\caption{Specifying property in Verilog design with output wire} +\label{specifying_property_output} +\end{figure} + +We can run Boolector~\cite{boolector}~$1.4.1$\footnote{ +Newer version of Boolector do not support sequential models. +Boolector 1.4.1 can be built with picosat-951. Newer versions +of picosat have an incompatible API.} on the generated BTOR +file: + +\begin{figure}[H] +\begin{lstlisting}[language=sh,numbers=none] +$ boolector fsm.btor +unsat +\end{lstlisting} + \renewcommand{\figurename}{Listing} +\caption{Running boolector on BTOR file} +\end{figure} + +We can also use nuXmv~\cite{nuxmv}, but on BTOR designs it does not +support memories yet. With the next release of nuXmv, we will be also +able to verify designs with memories. + +\section{Detailed Flow} + +Yosys is able to synthesize Verilog designs up to the gate level. +We are interested in keeping registers and memories when synthesizing +the design. For this purpose, we describe a customized Yosys synthesis +flow, that is also provided by the {\tt verilog2btor.sh} script. +Listing~\ref{btor_script_memory} shows the Yosys commands that are +executed by {\tt verilog2btor.sh}. + +\begin{figure}[H] +\begin{lstlisting}[language=sh] +read_verilog -sv $1; +hierarchy -top $3; hierarchy -libdir $DIR; +hierarchy -check; +proc; opt; +opt_expr -mux_undef; opt; +rename -hide;;; +splice; opt; +memory_dff -wr_only; memory_collect;; +flatten;; +memory_unpack; +splitnets -driver; +setundef -zero -undriven; +opt;;; +write_btor $2; +\end{lstlisting} + \renewcommand{\figurename}{Listing} +\caption{Synthesis Flow for BTOR with memories} +\label{btor_script_memory} +\end{figure} + +Here is short description of what is happening in the script line by +line: + +\begin{enumerate} +\item Reading the input file. +\item Setting the top module in the hierarchy and trying to read + automatically the files which are given as {\tt include} in the file + read in first line. +\item Checking the design hierarchy. +\item Converting processes to multiplexers (muxs) and flip-flops. +\item Removing undef signals from muxs. +\item Hiding all signal names that are not used as module ports. +\item Explicit type conversion, by introducing slice and concat cells + in the circuit. +\item Converting write memories to synchronous memories, and + collecting the memories to multi-port memories. +\item Flattening the design to get only one module. +\item Separating read and write memories. +\item Splitting the signals that are partially assigned +\item Setting undef to zero value. +\item Final optimization pass. +\item Writing BTOR file. +\end{enumerate} + +For detailed description of the commands mentioned above, please refer +to the Yosys documentation, or run {\tt yosys -h \it command\_name}. + +The script presented earlier can be easily modified to have a BTOR +file that does not contain memories. This is done by removing the line +number~8 and 10, and introduces a new command {\tt memory} at line +number~8. Listing~\ref{btor_script_without_memory} shows the +modified Yosys script file: + +\begin{figure}[H] +\begin{lstlisting}[language=sh,numbers=none] +read_verilog -sv $1; +hierarchy -top $3; hierarchy -libdir $DIR; +hierarchy -check; +proc; opt; +opt_expr -mux_undef; opt; +rename -hide;;; +splice; opt; +memory;; +flatten;; +splitnets -driver; +setundef -zero -undriven; +opt;;; +write_btor $2; +\end{lstlisting} + \renewcommand{\figurename}{Listing} +\caption{Synthesis Flow for BTOR without memories} +\label{btor_script_without_memory} +\end{figure} + +\section{Example} + +Here is an example Verilog design that we want to convert to BTOR: + +\begin{figure}[H] +\begin{lstlisting}[language=Verilog,numbers=none] +module array(input clk); + + reg [7:0] counter; + reg [7:0] mem [7:0]; + + always @(posedge clk) begin + counter <= counter + 8'd1; + mem[counter] <= counter; + end + + assert property (!(counter > 8'd0) || + mem[counter - 8'd1] == counter - 8'd1); + +endmodule +\end{lstlisting} +\renewcommand{\figurename}{Listing} +\caption{Example - Verilog Design} +\label{example_verilog} +\end{figure} + +The generated BTOR file that contain memories, using the script shown +in Listing~\ref{btor_script_memory}: +\begin{figure}[H] +\begin{lstlisting}[numbers=none] +1 var 1 clk +2 array 8 3 +3 var 8 $auto$rename.cc:150:execute$20 +4 const 8 00000001 +5 sub 8 3 4 +6 slice 3 5 2 0 +7 read 8 2 6 +8 slice 3 3 2 0 +9 add 8 3 4 +10 const 8 00000000 +11 ugt 1 3 10 +12 not 1 11 +13 const 8 11111111 +14 slice 1 13 0 0 +15 one 1 +16 eq 1 1 15 +17 and 1 16 14 +18 write 8 3 2 8 3 +19 acond 8 3 17 18 2 +20 anext 8 3 2 19 +21 eq 1 7 5 +22 or 1 12 21 +23 const 1 1 +24 one 1 +25 eq 1 23 24 +26 cond 1 25 22 24 +27 root 1 -26 +28 cond 8 1 9 3 +29 next 8 3 28 +\end{lstlisting} +\renewcommand{\figurename}{Listing} +\caption{Example - Converted BTOR with memory} +\label{example_btor} +\end{figure} + +And the BTOR file obtained by the script shown in +Listing~\ref{btor_script_without_memory}, which expands the memory +into individual elements: +\begin{figure}[H] +\begin{lstlisting}[numbers=none,escapechar=@] +1 var 1 clk +2 var 8 mem[0] +3 var 8 $auto$rename.cc:150:execute$20 +4 slice 3 3 2 0 +5 slice 1 4 0 0 +6 not 1 5 +7 slice 1 4 1 1 +8 not 1 7 +9 slice 1 4 2 2 +10 not 1 9 +11 and 1 8 10 +12 and 1 6 11 +13 cond 8 12 3 2 +14 cond 8 1 13 2 +15 next 8 2 14 +16 const 8 00000001 +17 add 8 3 16 +18 const 8 00000000 +19 ugt 1 3 18 +20 not 1 19 +21 var 8 mem[2] +22 and 1 7 10 +23 and 1 6 22 +24 cond 8 23 3 21 +25 cond 8 1 24 21 +26 next 8 21 25 +27 sub 8 3 16 + +@\vbox to 0pt{\vss\vdots\vskip3pt}@ +54 cond 1 53 50 52 +55 root 1 -54 + +@\vbox to 0pt{\vss\vdots\vskip3pt}@ +77 cond 8 76 3 44 +78 cond 8 1 77 44 +79 next 8 44 78 +\end{lstlisting} +\renewcommand{\figurename}{Listing} +\caption{Example - Converted BTOR without memory} +\label{example_btor} +\end{figure} + +\section{Limitations} + +BTOR does not support initialization of memories and registers, i.e. they are +implicitly initialized to value zero, so the initial block for +memories need to be removed when converting to BTOR. It should +also be kept in consideration that BTOR does not support the {\tt x} or {\tt z} +values of Verilog. + +Another thing to bear in mind is that Yosys will convert multi-dimensional +memories to one-dimensional memories and address decoders. Therefore +out-of-bounds memory accesses can yield unexpected results. + +\section{Conclusion} + +Using the described flow, we can use Yosys to generate word-level +verification benchmarks with or without memories from Verilog designs. + +\begin{thebibliography}{9} + +\bibitem{yosys} +Clifford Wolf. The Yosys Open SYnthesis Suite. \\ +\url{http://www.clifford.at/yosys/} + +\bibitem{boolector} +Robert Brummayer and Armin Biere, Boolector: An Efficient SMT Solver for Bit-Vectors and Arrays\\ +\url{http://fmv.jku.at/boolector/} + +\bibitem{btor} +Robert Brummayer and Armin Biere and Florian Lonsing, BTOR: +Bit-Precise Modelling of Word-Level Problems for Model Checking\\ +\url{http://fmv.jku.at/papers/BrummayerBiereLonsing-BPR08.pdf} + +\bibitem{nuxmv} +Roberto Cavada and Alessandro Cimatti and Michele Dorigatti and +Alberto Griggio and Alessandro Mariotti and Andrea Micheli and Sergio +Mover and Marco Roveri and Stefano Tonetta, The nuXmv Symbolic Model +Checker\\ +\url{https://es-static.fbk.eu/tools/nuxmv/index.php} + +\end{thebibliography} + + +\end{document} diff --git a/manual/CHAPTER_Appnotes.tex b/manual/CHAPTER_Appnotes.tex index 2abfa85da..e0d093290 100644 --- a/manual/CHAPTER_Appnotes.tex +++ b/manual/CHAPTER_Appnotes.tex @@ -5,9 +5,9 @@ % \begin{fixme} % This appendix will cover some typical use-cases of Yosys in the form of application notes. % \end{fixme} -% +% % \section{Synthesizing using a Cell Library in Liberty Format} -% \section{Reverse Engeneering the MOS6502 from an NMOS Transistor Netlist} +% \section{Reverse Engineering the MOS6502 from an NMOS Transistor Netlist} % \section{Reconfigurable Coarse-Grain Synthesis using Intersynth} This appendix contains copies of the Yosys application notes. @@ -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/CHAPTER_Approach.tex b/manual/CHAPTER_Approach.tex index 691225805..4b170ee0a 100644 --- a/manual/CHAPTER_Approach.tex +++ b/manual/CHAPTER_Approach.tex @@ -59,7 +59,7 @@ script. \section{Internal Formats in Yosys} -Yosys uses two different internal formats. The first is used to store an abstract syntax tree (AST) of a verilog +Yosys uses two different internal formats. The first is used to store an abstract syntax tree (AST) of a Verilog input file. This format is simply called {\it AST} and is generated by the Verilog Frontend. This data structure is consumed by a subsystem called {\it AST Frontend}\footnote{In Yosys the term {\it pass} is only used to refer to commands that operate on the RTLIL data structure.}. This AST Frontend then generates a design in Yosys' @@ -107,7 +107,7 @@ from the input file {\tt design.v} to a gate-level netlist {\tt synth.v} using t described by the Liberty file \citeweblink{LibertyFormat} {\tt cells.lib}: \begin{lstlisting}[language=sh,numbers=left,frame=single] -# read input file tpo internal representation +# read input file to internal representation read_verilog design.v # convert high-level behavioral parts ("processes") to d-type flip-flops and muxes diff --git a/manual/CHAPTER_Auxlibs.tex b/manual/CHAPTER_Auxlibs.tex index 8d3ed7430..440ea1375 100644 --- a/manual/CHAPTER_Auxlibs.tex +++ b/manual/CHAPTER_Auxlibs.tex @@ -1,7 +1,7 @@ -\chapter{Auxilary Libraries} +\chapter{Auxiliary Libraries} -The Yosys source distribution contains some auxilary libraries that are bundled +The Yosys source distribution contains some auxiliary libraries that are bundled with Yosys. \section{SHA1} diff --git a/manual/CHAPTER_Auxprogs.tex b/manual/CHAPTER_Auxprogs.tex index a00893828..724d37f0b 100644 --- a/manual/CHAPTER_Auxprogs.tex +++ b/manual/CHAPTER_Auxprogs.tex @@ -1,5 +1,5 @@ -\chapter{Auxilary Programs} +\chapter{Auxiliary Programs} Besides the main {\tt yosys} executable, the Yosys distribution contains a set of additional helper programs. diff --git a/manual/CHAPTER_Basics.tex b/manual/CHAPTER_Basics.tex index c0eda0e84..5c60b7305 100644 --- a/manual/CHAPTER_Basics.tex +++ b/manual/CHAPTER_Basics.tex @@ -56,8 +56,8 @@ and how they relate to different kinds of synthesis. Regardless of the way a lower level representation of a circuit is obtained (synthesis or manual design), the lower level representation is usually verified by comparing simulation results of the lower level and the higher level -representation \footnote{In recent years formal equivalence -checking also became an important verification method for validating RTL and +representation \footnote{In recent years formal equivalence +checking also became an important verification method for validating RTL and lower abstraction representation of the design.}. Therefore even if no synthesis is used, there must still be a simulatable representation of the circuit in all levels to allow for verification of the @@ -116,7 +116,7 @@ value or a condition in the sensitivity list is triggered. A synthesis tool must be able to transfer this representation into an appropriate datapath followed by the appropriate types of register. -For example consider the following verilog code fragment: +For example consider the following Verilog code fragment: \begin{lstlisting}[numbers=left,frame=single,language=Verilog] always @(posedge clk) @@ -141,8 +141,8 @@ App.~\ref{chapter:sota}). \subsection{Register-Transfer Level (RTL)} On the Register-Transfer Level the design is represented by combinatorial data -paths and registers (usually d-type flip flops). The following verilog code fragment -is equivalent to the previous verilog example, but is in RTL representation: +paths and registers (usually d-type flip flops). The following Verilog code fragment +is equivalent to the previous Verilog example, but is in RTL representation: \begin{lstlisting}[numbers=left,frame=single,language=Verilog] assign tmp = a + b; // combinatorial data path @@ -162,7 +162,7 @@ detection and optimization, identification of memories or other larger building and identification of shareable resources. Note that RTL is the first abstraction level in which the circuit is represented as a -graph of circuit elements (registers and combinatorical cells) and signals. Such a graph, +graph of circuit elements (registers and combinatorial cells) and signals. Such a graph, when encoded as list of cells and connections, is called a netlist. RTL synthesis is easy as each circuit node element in the netlist can simply be replaced @@ -262,15 +262,15 @@ Verilog syntax. Only the following language constructs are used in this case: \end{itemize} Many tools (especially at the back end of the synthesis chain) only support -structural verilog as input. ABC is an example of such a tool. Unfortunately +structural Verilog as input. ABC is an example of such a tool. Unfortunately there is no standard specifying what {\it Structural Verilog} actually is, leading to some confusion about what syntax constructs are supported in -structural verilog when it comes to features such as attributes or multi-bit +structural Verilog when it comes to features such as attributes or multi-bit signals. \subsection{Expressions in Verilog} -In all situations where Verilog accepts a constant value or signal name, +In all situations where Verilog accepts a constant value or signal name, expressions using arithmetic operations such as \lstinline[language=Verilog]{+}, \lstinline[language=Verilog]{-} and \lstinline[language=Verilog]{*}, boolean operations such as @@ -280,8 +280,8 @@ and many others (comparison operations, unary operator, etc.) can also be used. During synthesis these operators are replaced by cells that implement the respective function. Many FOSS tools that claim to be able to process Verilog in fact only support -basic structural verilog and simple expressions. Yosys can be used to convert -full featured synthesizable verilog to this simpler subset, thus enabling such +basic structural Verilog and simple expressions. Yosys can be used to convert +full featured synthesizable Verilog to this simpler subset, thus enabling such applications to be used with a richer set of Verilog features. \subsection{Behavioural Modelling} @@ -470,7 +470,7 @@ optimizes the design. First of all because not all optimizations are applicable designs and all synthesis tasks. Some optimizations work (best) on a coarse-grained level (with complex cells such as adders or multipliers) and others work (best) on a fine-grained level (single bit gates). Some optimizations target area and others target speed. -Some work well on large designs while others don't scale well and can only be applied +Some work well on large designs while others don't scale well and can only be applied to small designs. A good tool is capable of applying a wide range of optimizations at different @@ -561,7 +561,7 @@ In order to guarantee reproducibility it is important to be able to re-run all automatic steps in a design project with a fixed set of settings easily. Because of this, usually all programs used in a synthesis flow can be controlled using scripts. This means that all functions are available via -text commands. When such a tool provides a gui, this is complementary to, +text commands. When such a tool provides a GUI, this is complementary to, and not instead of, a command line interface. Usually a synthesis flow in an UNIX/Linux environment would be controlled by a diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex index 43d40c73f..f97d4ffa5 100644 --- a/manual/CHAPTER_CellLib.tex +++ b/manual/CHAPTER_CellLib.tex @@ -104,7 +104,7 @@ Multiplexers are generated by the Verilog HDL frontend for {\tt from RTLIL::Process objects to logic. The simplest multiplexer cell type is {\tt \$mux}. Cells of this type have a \B{WIDTH} parameter -and data inputs \B{A} and \B{B} and a data ouput \B{Y}, all of the specified width. This cell also +and data inputs \B{A} and \B{B} and a data output \B{Y}, all of the specified width. This cell also has a single bit control input \B{S}. If \B{S} is 0 the value from the \B{A} input is sent to the output, if it is 1 the value from the \B{B} input is sent to the output. So the {\tt \$mux} cell implements the function \lstinline[language=Verilog]; Y = S ? B : A;. @@ -220,8 +220,9 @@ cell is created. Having individual cells for read and write ports has the advant consolidated using resource sharing passes. In some cases this drastically reduces the number of required ports on the memory cell. -The {\tt \$memrd} cells have a clock input \B{CLK}, an address input \B{ADDR} and a data output -\B{DATA}. They also have the following parameters: +The {\tt \$memrd} cells have a clock input \B{CLK}, an enable input \B{EN}, an +address input \B{ADDR}, and a data output \B{DATA}. They also have the +following parameters: \begin{itemize} \item \B{MEMID} \\ @@ -322,6 +323,9 @@ The {\tt \$mem} cell has the following ports: \item \B{RD\_CLK} \\ This input is \B{RD\_PORTS} bits wide, containing all clock signals for the read ports. +\item \B{RD\_EN} \\ +This input is \B{RD\_PORTS} bits wide, containing all enable signals for the read ports. + \item \B{RD\_ADDR} \\ This input is \B{RD\_PORTS}*\B{ABITS} bits wide, containing all address signals for the read ports. @@ -399,7 +403,7 @@ represent d-type flip-flops. The cell types {\tt \$\_DFF\_NN0\_}, {\tt \$\_DFF\_NN1\_}, {\tt \$\_DFF\_NP0\_}, {\tt \$\_DFF\_NP1\_}, {\tt \$\_DFF\_PN0\_}, {\tt \$\_DFF\_PN1\_}, {\tt \$\_DFF\_PP0\_} and {\tt \$\_DFF\_PP1\_} implement d-type flip-flops with asynchronous resets. The values in the table for these cell types relate to the -following verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge; +following Verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge; if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, and \lstinline[language=Verilog];negedge; otherwise. @@ -417,7 +421,7 @@ pass. The combinatorial logic cells can be mapped to physical cells from a Liber using the {\tt abc} pass. \begin{fixme} -Add information about {\tt \$assert} and {\tt \$equiv} cells. +Add information about {\tt \$assert}, {\tt \$assume}, and {\tt \$equiv} cells. \end{fixme} \begin{fixme} @@ -425,6 +429,10 @@ Add information about {\tt \$slice} and {\tt \$concat} cells. \end{fixme} \begin{fixme} +Add information about {\tt \$lut} and {\tt \$sop} cells. +\end{fixme} + +\begin{fixme} Add information about {\tt \$alu}, {\tt \$macc}, {\tt \$fa}, and {\tt \$lcu} cells. \end{fixme} diff --git a/manual/CHAPTER_Eval.tex b/manual/CHAPTER_Eval.tex index c27a000bf..f719618d5 100644 --- a/manual/CHAPTER_Eval.tex +++ b/manual/CHAPTER_Eval.tex @@ -43,7 +43,7 @@ The following measures were taken to increase the confidence in the correctness make test} is executed. During development of Yosys it was shown that this collection of test cases is sufficient to catch most bugs. The following more sophisticated test procedures only caught a few additional bugs. Whenever this -happend, an appropiate test case was added to the collection of small test +happened, an appropriate test case was added to the collection of small test cases for {\tt make test} to ensure better testability of the feature in question in the future. @@ -64,7 +64,7 @@ validate successfully using Formality. \item VlogHammer \citeweblink{VlogHammer} is a set of scripts that auto-generate a large collection of test cases\footnote{At the time of this writing over 6600 test cases.} and synthesize them using Yosys and the -following freely available propritary synthesis tools. +following freely available proprietary synthesis tools. \begin{itemize} \item Xilinx Vivado WebPack (2013.2) \citeweblink{XilinxWebPACK} \item Xilinx ISE (XST) WebPack (14.5) \citeweblink{XilinxWebPACK} diff --git a/manual/CHAPTER_Eval/grep-it.sh b/manual/CHAPTER_Eval/grep-it.sh index f92eb52cf..0f4f95ae5 100644 --- a/manual/CHAPTER_Eval/grep-it.sh +++ b/manual/CHAPTER_Eval/grep-it.sh @@ -79,6 +79,6 @@ done # if [ $luts -gt 0 -a $luts_ys -gt 0 ]; then luts_p=$(( 100*luts_ys / luts )); else luts_p=NaN; fi # if [ $freq -gt 0 -a $freq_ys -gt 0 ]; then freq_p=$(( 100*freq_ys / freq )); else freq_p=NaN; fi # printf '%-30s %3s %3s %3s\n' $mod $regs_p $luts_p $freq_p -# +# # done diff --git a/manual/CHAPTER_Intro.tex b/manual/CHAPTER_Intro.tex index f735d46b2..76e5d847b 100644 --- a/manual/CHAPTER_Intro.tex +++ b/manual/CHAPTER_Intro.tex @@ -35,7 +35,7 @@ The proposed custom HDL synthesis tool should be licensed under a Free and Open Source Software (FOSS) licence. So an existing FOSS Verilog or VHDL synthesis tool would have been needed as basis to build upon. The main advantages of choosing Verilog or VHDL is the ability to synthesize existing HDL code and -to mitigate the requirement for circuit-designers to learn a new language. In order to take full advantage of any existing FOSS Verilog or VHDL tool, +to mitigate the requirement for circuit-designers to learn a new language. In order to take full advantage of any existing FOSS Verilog or VHDL tool, such a tool would have to provide a feature-complete implementation of the synthesizable HDL subset. @@ -68,7 +68,7 @@ problem of implementing a HDL synthesis tool is approached in the case of Yosys. Chapter~\ref{chapter:overview} contains a more detailed overview of the -implementation of Yosys. This chapter covers the data structures used in +implementation of Yosys. This chapter covers the data structures used in Yosys to represent a design in detail and is therefore recommended reading for everyone who is interested in understanding the Yosys internals. @@ -81,7 +81,7 @@ is recommended reading for everyone who actually wants to read or write Yosys source code. The chapter concludes with an example loadable module for Yosys. -Chapters~\ref{chapter:verilog}, \ref{chapter:opt}, and \ref{chapter:techmap} +Chapters~\ref{chapter:verilog}, \ref{chapter:opt}, and \ref{chapter:techmap} cover three important pieces of the synthesis pipeline: The Verilog frontend, the optimization passes and the technology mapping to the target architecture, respectively. diff --git a/manual/CHAPTER_Optimize.tex b/manual/CHAPTER_Optimize.tex index af8e22497..4b09c2231 100644 --- a/manual/CHAPTER_Optimize.tex +++ b/manual/CHAPTER_Optimize.tex @@ -15,23 +15,23 @@ passes that each perform a simple optimization: \begin{itemize} \item Once at the beginning of {\tt opt}: \begin{itemize} -\item {\tt opt\_const} -\item {\tt opt\_share -nomux} +\item {\tt opt\_expr} +\item {\tt opt\_merge -nomux} \end{itemize} \item Repeat until result is stable: \begin{itemize} \item {\tt opt\_muxtree} \item {\tt opt\_reduce} -\item {\tt opt\_share} +\item {\tt opt\_merge} \item {\tt opt\_rmdff} \item {\tt opt\_clean} -\item {\tt opt\_const} +\item {\tt opt\_expr} \end{itemize} \end{itemize} The following section describes each of the {\tt opt\_*} passes. -\subsection{The opt\_const pass} +\subsection{The opt\_expr pass} This pass performs const folding on the internal combinational cell types described in Chap.~\ref{chapter:celllib}. This means a cell with all constant @@ -57,11 +57,11 @@ this pass can also optimize cells with some constant inputs. $a$ & 1 & $a$ \\ 1 & $b$ & $b$ \\ \end{tabular} - \caption{Const folding rules for {\tt\$\_AND\_} cells as used in {\tt opt\_const}.} - \label{tab:opt_const_and} + \caption{Const folding rules for {\tt\$\_AND\_} cells as used in {\tt opt\_expr}.} + \label{tab:opt_expr_and} \end{table} -Table~\ref{tab:opt_const_and} shows the replacement rules used for optimizing +Table~\ref{tab:opt_expr_and} shows the replacement rules used for optimizing an {\tt\$\_AND\_} gate. The first three rules implement the obvious const folding rules. Note that `any' might include dynamic values calculated by other parts of the circuit. The following three lines propagate undef (X) states. @@ -69,17 +69,17 @@ These are the only three cases in which it is allowed to propagate an undef according to Sec.~5.1.10 of IEEE Std. 1364-2005 \cite{Verilog2005}. The next two lines assume the value 0 for undef states. These two rules are only -used if no other subsitutions are possible in the current module. If other substitutions +used if no other substitutions are possible in the current module. If other substitutions are possible they are performed first, in the hope that the `any' will change to an undef value or a 1 and therefore the output can be set to undef. The last two lines simply replace an {\tt\$\_AND\_} gate with one constant-1 input with a buffer. -Besides this basic const folding the {\tt opt\_const} pass can replace 1-bit wide +Besides this basic const folding the {\tt opt\_expr} pass can replace 1-bit wide {\tt \$eq} and {\tt \$ne} cells with buffers or not-gates if one input is constant. -The {\tt opt\_const} pass is very conservative regarding optimizing {\tt \$mux} cells, +The {\tt opt\_expr} pass is very conservative regarding optimizing {\tt \$mux} cells, as these cells are often used to model decision-trees and breaking these trees can interfere with other optimizations. @@ -130,7 +130,7 @@ This pass identifies unused signals and cells and removes them from the design. creates an \B{unused\_bits} attribute on wires with unused bits. This attribute can be used for debugging or by other optimization passes. -\subsection{The opt\_share pass} +\subsection{The opt\_merge pass} This pass performs trivial resource sharing. This means that this pass identifies cells with identical inputs and replaces them with a single instance of the cell. @@ -241,7 +241,7 @@ by identifying the driver for the state signal. From there the {\tt \$mux}-tree driving the state register inputs is recursively traversed. All select inputs are control signals and the leaves of the -{\tt \$mux}-tree are the states. The algorithm fails if a non-constant leaf +{\tt \$mux}-tree are the states. The algorithm fails if a non-constant leaf that is not the state signal itself is found. The list of control outputs is initialized with the bits from the state signal. @@ -296,7 +296,7 @@ table altered to give the same performance without the external feedback path. \item Entries in the transition table that yield the same output and only differ in the value of a single control input bit are merged and the different bit is removed from the sensitivity list (turned into a don't-care bit). -\item Constant inputs are removed and the transition table is alterered to give an unchanged behaviour. +\item Constant inputs are removed and the transition table is altered to give an unchanged behaviour. \item Unused inputs are removed. \end{itemize} diff --git a/manual/CHAPTER_Overview.tex b/manual/CHAPTER_Overview.tex index ec402231f..964875d57 100644 --- a/manual/CHAPTER_Overview.tex +++ b/manual/CHAPTER_Overview.tex @@ -238,7 +238,7 @@ An RTLIL::Wire object has the following properties: \end{itemize} As with modules, the attributes can be Verilog attributes imported by the -Verilog frontend or attributes assigned by passees. +Verilog frontend or attributes assigned by passes. In Yosys, busses (signal vectors) are represented using a single wire object with a width > 1. So Yosys does not convert signal vectors to individual signals. @@ -307,11 +307,11 @@ process $proc$ff_with_en_and_async_reset.v:4$1 switch \reset case 1'1 assign $0\q[0:0] 1'0 - case + case switch \enable case 1'1 assign $0\q[0:0] \d - case + case end end sync posedge \clock @@ -338,7 +338,7 @@ An RTLIL::CaseRule is a container for zero or more assignments (RTLIL::SigSig) and zero or more RTLIL::SwitchRule objects. An RTLIL::SwitchRule objects is a container for zero or more RTLIL::CaseRule objects. -In the above example the lines $2 \dots 12$ are the root case. Here {\tt \$0\textbackslash{}q[0:0]} is first +In the above example the lines $2 \dots 12$ are the root case. Here {\tt \$0\textbackslash{}q[0:0]} is first assigned the old value {\tt \textbackslash{}q} as default value (line 2). The root case also contains an RTLIL::SwitchRule object (lines $3 \dots 12$). Such an object is very similar to the C {\tt switch} statement as it uses a control signal ({\tt \textbackslash{}reset} in this case) to determine @@ -371,7 +371,7 @@ process $proc$ff_with_en_and_async_reset.v:4$1 switch \enable case 1'1 assign $0\q[0:0] \d - case + case end sync posedge \clock update \q $0\q[0:0] @@ -412,7 +412,7 @@ Some passes refuse to operate on modules that still contain RTLIL::Process objec presence of these objects in a module increases the complexity. Therefore the passes to translate processes to a netlist of cells are usually called early in a synthesis script. The {\tt proc} pass calls a series of other passes that together perform this conversion in a way that is suitable -for most synthesis taks. +for most synthesis tasks. \subsection{RTLIL::Memory} @@ -449,7 +449,7 @@ See Sec.~\ref{sec:memcells} for details about the memory cell types. Yosys reads and processes commands from synthesis scripts, command line arguments and an interactive command prompt. Yosys commands consist of a command name and an optional whitespace separated list of arguments. Commands are terminated using the newline character -or a semicolon ({\tt ;}). Empty lines and lines starting with the hash sign ({\tt \#}) are ignored. +or a semicolon ({\tt ;}). Empty lines and lines starting with the hash sign ({\tt \#}) are ignored. See Sec.~\ref{sec:typusecase} for an example synthesis script. The command {\tt help} can be used to access the command reference manual. @@ -488,8 +488,8 @@ select.cc}, {\tt show.cc}, \dots) and a couple of other small utility libraries. \item {\tt passes/} \\ This directory contains a subdirectory for each pass or group of passes. For example as of this writing the directory {\tt passes/opt/} contains the code for seven -passes: {\tt opt}, {\tt opt\_const}, {\tt opt\_muxtree}, {\tt opt\_reduce}, -{\tt opt\_rmdff}, {\tt opt\_rmunused} and {\tt opt\_share}. +passes: {\tt opt}, {\tt opt\_expr}, {\tt opt\_muxtree}, {\tt opt\_reduce}, +{\tt opt\_rmdff}, {\tt opt\_rmunused} and {\tt opt\_merge}. \item {\tt techlibs/} \\ This directory contains simulation models and standard implementations for the @@ -513,7 +513,7 @@ Yosys. So it is not needed to add additional commands to a central list of comma \end{sloppypar} Good starting points for reading example source code to learn how to write passes -are {\tt passes/opt/opt\_rmdff.cc} and {\tt passes/opt/opt\_share.cc}. +are {\tt passes/opt/opt\_rmdff.cc} and {\tt passes/opt/opt\_merge.cc}. See the top-level README file for a quick {\it Getting Started} guide and build instructions. The Yosys build is based solely on Makefiles. diff --git a/manual/CHAPTER_Prog/stubnets.cc b/manual/CHAPTER_Prog/stubnets.cc index 4849c6a7b..eb77bd404 100644 --- a/manual/CHAPTER_Prog/stubnets.cc +++ b/manual/CHAPTER_Prog/stubnets.cc @@ -1,5 +1,5 @@ // This is free and unencumbered software released into the public domain. -// +// // Anyone is free to copy, modify, publish, use, compile, sell, or // distribute this software, either in source code form or as a compiled // binary, for any purpose, commercial or non-commercial, and by any @@ -24,7 +24,7 @@ static void find_stub_nets(RTLIL::Design *design, RTLIL::Module *module, bool re // count how many times a single-bit signal is used std::map<RTLIL::SigBit, int> bit_usage_count; - // count ouput lines for this module (needed only for summary output at the end) + // count output lines for this module (needed only for summary output at the end) int line_count = 0; log("Looking for stub wires in module %s:\n", RTLIL::id2cstr(module->name)); @@ -103,7 +103,7 @@ struct StubnetsPass : public Pass { // variables to mirror information from passed options bool report_bits = 0; - log_header("Executing STUBNETS pass (find stub nets).\n"); + log_header(design, "Executing STUBNETS pass (find stub nets).\n"); // parse options size_t argidx; diff --git a/manual/CHAPTER_StateOfTheArt.tex b/manual/CHAPTER_StateOfTheArt.tex index 7e62230ef..2d0c77a01 100644 --- a/manual/CHAPTER_StateOfTheArt.tex +++ b/manual/CHAPTER_StateOfTheArt.tex @@ -248,7 +248,7 @@ passes). This architecture will simplify implementing additional HDL front ends and/or additional synthesis passes. Chapter~\ref{chapter:eval} contains a more detailed evaluation of Yosys using real-world -designes that are far out of reach for any of the other tools discussed in this appendix. +designs that are far out of reach for any of the other tools discussed in this appendix. \vskip2cm \begin{table}[h] diff --git a/manual/CHAPTER_StateOfTheArt/simlib_hana.v b/manual/CHAPTER_StateOfTheArt/simlib_hana.v index fc82f1389..7fb54fa49 100644 --- a/manual/CHAPTER_StateOfTheArt/simlib_hana.v +++ b/manual/CHAPTER_StateOfTheArt/simlib_hana.v @@ -1,4 +1,4 @@ -/* +/* Copyright (C) 2009-2010 Parvez Ahmad Written by Parvez Ahmad <parvez_ahmad@yahoo.co.uk>. @@ -45,7 +45,7 @@ module AND3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); assign out = ∈ endmodule - + module AND4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); assign out = ∈ @@ -63,7 +63,7 @@ module OR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); assign out = |in; endmodule - + module OR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); assign out = |in; @@ -82,7 +82,7 @@ module NAND3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); assign out = ~∈ endmodule - + module NAND4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); assign out = ~∈ @@ -100,7 +100,7 @@ module NOR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); assign out = ~|in; endmodule - + module NOR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); assign out = ~|in; @@ -119,7 +119,7 @@ module XOR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); assign out = ^in; endmodule - + module XOR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); assign out = ^in; @@ -138,7 +138,7 @@ module XNOR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); assign out = ~^in; endmodule - + module XNOR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); assign out = ~^in; @@ -156,7 +156,7 @@ always @(in or enable) 1'b1 : out = 2'b10; endcase end -endmodule +endmodule module DEC2 (input [1:0] in, input enable, output reg [3:0] out); @@ -171,7 +171,7 @@ always @(in or enable) 2'b11 : out = 4'b1000; endcase end -endmodule +endmodule module DEC3 (input [2:0] in, input enable, output reg [7:0] out); @@ -190,7 +190,7 @@ always @(in or enable) 3'b111 : out = 8'b10000000; endcase end -endmodule +endmodule module DEC4 (input [3:0] in, input enable, output reg [15:0] out); @@ -217,7 +217,7 @@ always @(in or enable) 4'b1111 : out = 16'b1000000000000000; endcase end -endmodule +endmodule module DEC5 (input [4:0] in, input enable, output reg [31:0] out); always @(in or enable) @@ -259,7 +259,7 @@ always @(in or enable) 5'b11111 : out = 32'b10000000000000000000000000000000; endcase end -endmodule +endmodule module DEC6 (input [5:0] in, input enable, output reg [63:0] out); @@ -335,7 +335,7 @@ always @(in or enable) 6'b111111 : out = 64'b1000000000000000000000000000000000000000000000000000000000000000; endcase end -endmodule +endmodule module MUX2(input [1:0] in, input select, output reg out); @@ -345,7 +345,7 @@ always @( in or select) 0: out = in[0]; 1: out = in[1]; endcase -endmodule +endmodule module MUX4(input [3:0] in, input [1:0] select, output reg out); @@ -357,7 +357,7 @@ always @( in or select) 2: out = in[2]; 3: out = in[3]; endcase -endmodule +endmodule module MUX8(input [7:0] in, input [2:0] select, output reg out); @@ -373,7 +373,7 @@ always @( in or select) 6: out = in[6]; 7: out = in[7]; endcase -endmodule +endmodule module MUX16(input [15:0] in, input [3:0] select, output reg out); @@ -396,7 +396,7 @@ always @( in or select) 14: out = in[14]; 15: out = in[15]; endcase -endmodule +endmodule module MUX32(input [31:0] in, input [4:0] select, output reg out); @@ -435,7 +435,7 @@ always @( in or select) 30: out = in[30]; 31: out = in[31]; endcase -endmodule +endmodule module MUX64(input [63:0] in, input [5:0] select, output reg out); @@ -506,7 +506,7 @@ always @( in or select) 62: out = in[62]; 63: out = in[63]; endcase -endmodule +endmodule module ADD1(input in1, in2, cin, output out, cout); @@ -514,41 +514,41 @@ assign {cout, out} = in1 + in2 + cin; endmodule -module ADD2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, +module ADD2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, input cin, output [SIZE-1:0] out, output cout); assign {cout, out} = in1 + in2 + cin; endmodule -module ADD4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, +module ADD4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, input cin, output [SIZE-1:0] out, output cout); assign {cout, out} = in1 + in2 + cin; endmodule -module ADD8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, +module ADD8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, input cin, output [SIZE-1:0] out, output cout); assign {cout, out} = in1 + in2 + cin; endmodule -module ADD16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, +module ADD16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, input cin, output [SIZE-1:0] out, output cout); assign {cout, out} = in1 + in2 + cin; endmodule -module ADD32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, +module ADD32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, input cin, output [SIZE-1:0] out, output cout); assign {cout, out} = in1 + in2 + cin; endmodule -module ADD64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, +module ADD64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, input cin, output [SIZE-1:0] out, output cout); assign {cout, out} = in1 + in2 + cin; @@ -561,41 +561,41 @@ assign {cout, out} = in1 - in2 - cin; endmodule -module SUB2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, +module SUB2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, input cin, output [SIZE-1:0] out, output cout); assign {cout, out} = in1 - in2 - cin; endmodule -module SUB4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, +module SUB4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, input cin, output [SIZE-1:0] out, output cout); assign {cout, out} = in1 - in2 - cin; endmodule -module SUB8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, +module SUB8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, input cin, output [SIZE-1:0] out, output cout); assign {cout, out} = in1 - in2 - cin; endmodule -module SUB16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, +module SUB16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, input cin, output [SIZE-1:0] out, output cout); assign {cout, out} = in1 - in2 - cin; endmodule -module SUB32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, +module SUB32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, input cin, output [SIZE-1:0] out, output cout); assign {cout, out} = in1 - in2 - cin; endmodule -module SUB64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, +module SUB64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, input cin, output [SIZE-1:0] out, output cout); assign {cout, out} = in1 - in2 - cin; @@ -651,7 +651,7 @@ assign rem = in1%in2; endmodule -module DIV2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, +module DIV2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, output [SIZE-1:0] out, rem); assign out = in1/in2; @@ -659,7 +659,7 @@ assign rem = in1%in2; endmodule -module DIV4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, +module DIV4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, output [SIZE-1:0] out, rem); assign out = in1/in2; @@ -667,7 +667,7 @@ assign rem = in1%in2; endmodule -module DIV8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, +module DIV8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, output [SIZE-1:0] out, rem); assign out = in1/in2; @@ -675,7 +675,7 @@ assign rem = in1%in2; endmodule -module DIV16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, +module DIV16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, output [SIZE-1:0] out, rem); assign out = in1/in2; @@ -683,7 +683,7 @@ assign rem = in1%in2; endmodule -module DIV32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, +module DIV32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, output [SIZE-1:0] out, rem); assign out = in1/in2; @@ -691,7 +691,7 @@ assign rem = in1%in2; endmodule -module DIV64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, +module DIV64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, output [SIZE-1:0] out, rem); assign out = in1/in2; @@ -711,7 +711,7 @@ always @(posedge clk or posedge reset) q <= 0; else q <= d; -endmodule +endmodule module SFF(input d, clk, set, output reg q); always @(posedge clk or posedge set) @@ -719,7 +719,7 @@ always @(posedge clk or posedge set) q <= 1; else q <= d; -endmodule +endmodule module RSFF(input d, clk, set, reset, output reg q); always @(posedge clk or posedge reset or posedge set) @@ -745,30 +745,30 @@ module LATCH(input d, enable, output reg q); always @( d or enable) if(enable) q <= d; -endmodule +endmodule module RLATCH(input d, reset, enable, output reg q); always @( d or enable or reset) if(enable) if(reset) q <= 0; - else + else q <= d; -endmodule +endmodule module LSHIFT1 #(parameter SIZE = 1)(input in, shift, val, output reg out); always @ (in, shift, val) begin if(shift) out = val; - else + else out = in; end endmodule -module LSHIFT2 #(parameter SIZE = 2)(input [SIZE-1:0] in, +module LSHIFT2 #(parameter SIZE = 2)(input [SIZE-1:0] in, input [SIZE-1:0] shift, input val, output reg [SIZE-1:0] out); @@ -776,58 +776,58 @@ always @(in or shift or val) begin out = in << shift; if(val) out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); -end +end endmodule -module LSHIFT4 #(parameter SIZE = 4)(input [SIZE-1:0] in, +module LSHIFT4 #(parameter SIZE = 4)(input [SIZE-1:0] in, input [2:0] shift, input val, output reg [SIZE-1:0] out); always @(in or shift or val) begin out = in << shift; if(val) out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); -end +end endmodule -module LSHIFT8 #(parameter SIZE = 8)(input [SIZE-1:0] in, +module LSHIFT8 #(parameter SIZE = 8)(input [SIZE-1:0] in, input [3:0] shift, input val, output reg [SIZE-1:0] out); always @(in or shift or val) begin out = in << shift; if(val) out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); -end +end endmodule -module LSHIFT16 #(parameter SIZE = 16)(input [SIZE-1:0] in, +module LSHIFT16 #(parameter SIZE = 16)(input [SIZE-1:0] in, input [4:0] shift, input val, output reg [SIZE-1:0] out); always @(in or shift or val) begin out = in << shift; if(val) out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); -end +end endmodule -module LSHIFT32 #(parameter SIZE = 32)(input [SIZE-1:0] in, +module LSHIFT32 #(parameter SIZE = 32)(input [SIZE-1:0] in, input [5:0] shift, input val, output reg [SIZE-1:0] out); always @(in or shift or val) begin out = in << shift; if(val) out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); -end +end endmodule -module LSHIFT64 #(parameter SIZE = 64)(input [SIZE-1:0] in, +module LSHIFT64 #(parameter SIZE = 64)(input [SIZE-1:0] in, input [6:0] shift, input val, output reg [SIZE-1:0] out); always @(in or shift or val) begin out = in << shift; if(val) out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); -end +end endmodule module RSHIFT1 #(parameter SIZE = 1)(input in, shift, val, output reg out); @@ -841,7 +841,7 @@ end endmodule -module RSHIFT2 #(parameter SIZE = 2)(input [SIZE-1:0] in, +module RSHIFT2 #(parameter SIZE = 2)(input [SIZE-1:0] in, input [SIZE-1:0] shift, input val, output reg [SIZE-1:0] out); @@ -849,12 +849,12 @@ always @(in or shift or val) begin out = in >> shift; if(val) out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); -end +end endmodule -module RSHIFT4 #(parameter SIZE = 4)(input [SIZE-1:0] in, +module RSHIFT4 #(parameter SIZE = 4)(input [SIZE-1:0] in, input [2:0] shift, input val, output reg [SIZE-1:0] out); @@ -862,10 +862,10 @@ always @(in or shift or val) begin out = in >> shift; if(val) out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); -end +end endmodule -module RSHIFT8 #(parameter SIZE = 8)(input [SIZE-1:0] in, +module RSHIFT8 #(parameter SIZE = 8)(input [SIZE-1:0] in, input [3:0] shift, input val, output reg [SIZE-1:0] out); @@ -873,11 +873,11 @@ always @(in or shift or val) begin out = in >> shift; if(val) out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); -end +end endmodule -module RSHIFT16 #(parameter SIZE = 16)(input [SIZE-1:0] in, +module RSHIFT16 #(parameter SIZE = 16)(input [SIZE-1:0] in, input [4:0] shift, input val, output reg [SIZE-1:0] out); @@ -885,11 +885,11 @@ always @(in or shift or val) begin out = in >> shift; if(val) out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); -end +end endmodule -module RSHIFT32 #(parameter SIZE = 32)(input [SIZE-1:0] in, +module RSHIFT32 #(parameter SIZE = 32)(input [SIZE-1:0] in, input [5:0] shift, input val, output reg [SIZE-1:0] out); @@ -897,10 +897,10 @@ always @(in or shift or val) begin out = in >> shift; if(val) out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); -end +end endmodule -module RSHIFT64 #(parameter SIZE = 64)(input [SIZE-1:0] in, +module RSHIFT64 #(parameter SIZE = 64)(input [SIZE-1:0] in, input [6:0] shift, input val, output reg [SIZE-1:0] out); @@ -908,10 +908,10 @@ always @(in or shift or val) begin out = in >> shift; if(val) out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); -end +end endmodule -module CMP1 #(parameter SIZE = 1) (input in1, in2, +module CMP1 #(parameter SIZE = 1) (input in1, in2, output reg equal, unequal, greater, lesser); always @ (in1 or in2) begin @@ -920,7 +920,7 @@ always @ (in1 or in2) begin unequal = 0; greater = 0; lesser = 0; - end + end else begin equal = 0; unequal = 1; @@ -928,17 +928,17 @@ always @ (in1 or in2) begin if(in1 < in2) begin greater = 0; lesser = 1; - end + end else begin greater = 1; lesser = 0; - end - end + end + end end endmodule -module CMP2 #(parameter SIZE = 2) (input [SIZE-1:0] in1, in2, +module CMP2 #(parameter SIZE = 2) (input [SIZE-1:0] in1, in2, output reg equal, unequal, greater, lesser); always @ (in1 or in2) begin @@ -947,7 +947,7 @@ always @ (in1 or in2) begin unequal = 0; greater = 0; lesser = 0; - end + end else begin equal = 0; unequal = 1; @@ -955,16 +955,16 @@ always @ (in1 or in2) begin if(in1 < in2) begin greater = 0; lesser = 1; - end + end else begin greater = 1; lesser = 0; - end - end + end + end end endmodule -module CMP4 #(parameter SIZE = 4) (input [SIZE-1:0] in1, in2, +module CMP4 #(parameter SIZE = 4) (input [SIZE-1:0] in1, in2, output reg equal, unequal, greater, lesser); always @ (in1 or in2) begin @@ -973,7 +973,7 @@ always @ (in1 or in2) begin unequal = 0; greater = 0; lesser = 0; - end + end else begin equal = 0; unequal = 1; @@ -981,16 +981,16 @@ always @ (in1 or in2) begin if(in1 < in2) begin greater = 0; lesser = 1; - end + end else begin greater = 1; lesser = 0; - end - end + end + end end endmodule -module CMP8 #(parameter SIZE = 8) (input [SIZE-1:0] in1, in2, +module CMP8 #(parameter SIZE = 8) (input [SIZE-1:0] in1, in2, output reg equal, unequal, greater, lesser); always @ (in1 or in2) begin @@ -999,7 +999,7 @@ always @ (in1 or in2) begin unequal = 0; greater = 0; lesser = 0; - end + end else begin equal = 0; unequal = 1; @@ -1007,16 +1007,16 @@ always @ (in1 or in2) begin if(in1 < in2) begin greater = 0; lesser = 1; - end + end else begin greater = 1; lesser = 0; - end - end + end + end end endmodule -module CMP16 #(parameter SIZE = 16) (input [SIZE-1:0] in1, in2, +module CMP16 #(parameter SIZE = 16) (input [SIZE-1:0] in1, in2, output reg equal, unequal, greater, lesser); always @ (in1 or in2) begin @@ -1025,7 +1025,7 @@ always @ (in1 or in2) begin unequal = 0; greater = 0; lesser = 0; - end + end else begin equal = 0; unequal = 1; @@ -1033,16 +1033,16 @@ always @ (in1 or in2) begin if(in1 < in2) begin greater = 0; lesser = 1; - end + end else begin greater = 1; lesser = 0; - end - end + end + end end endmodule -module CMP32 #(parameter SIZE = 32) (input [SIZE-1:0] in1, in2, +module CMP32 #(parameter SIZE = 32) (input [SIZE-1:0] in1, in2, output reg equal, unequal, greater, lesser); always @ (in1 or in2) begin @@ -1051,7 +1051,7 @@ always @ (in1 or in2) begin unequal = 0; greater = 0; lesser = 0; - end + end else begin equal = 0; unequal = 1; @@ -1059,16 +1059,16 @@ always @ (in1 or in2) begin if(in1 < in2) begin greater = 0; lesser = 1; - end + end else begin greater = 1; lesser = 0; - end - end + end + end end endmodule -module CMP64 #(parameter SIZE = 64) (input [SIZE-1:0] in1, in2, +module CMP64 #(parameter SIZE = 64) (input [SIZE-1:0] in1, in2, output reg equal, unequal, greater, lesser); always @ (in1 or in2) begin @@ -1077,7 +1077,7 @@ always @ (in1 or in2) begin unequal = 0; greater = 0; lesser = 0; - end + end else begin equal = 0; unequal = 1; @@ -1085,12 +1085,12 @@ always @ (in1 or in2) begin if(in1 < in2) begin greater = 0; lesser = 1; - end + end else begin greater = 1; lesser = 0; - end - end + end + end end endmodule diff --git a/manual/CHAPTER_StateOfTheArt/simlib_yosys.v b/manual/CHAPTER_StateOfTheArt/simlib_yosys.v index 54c076614..454c9a83f 100644 --- a/manual/CHAPTER_StateOfTheArt/simlib_yosys.v +++ b/manual/CHAPTER_StateOfTheArt/simlib_yosys.v @@ -2,11 +2,11 @@ * 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 @@ -19,7 +19,7 @@ * * The internal logic cell simulation library. * - * This verilog library contains simple simulation models for the internal + * This Verilog library contains simple simulation models for the internal * logic cells (_NOT_, _AND_, ...) that are generated by the default technology * mapper (see "stdcells.v" in this directory) and expected by the "abc" pass. * diff --git a/manual/CHAPTER_Techmap.tex b/manual/CHAPTER_Techmap.tex index e5c7456c4..13aa8e5a3 100644 --- a/manual/CHAPTER_Techmap.tex +++ b/manual/CHAPTER_Techmap.tex @@ -5,7 +5,7 @@ Previous chapters outlined how HDL code is transformed into an RTL netlist. The RTL netlist is still based on abstract coarse-grain cell types like arbitrary width adders and even multipliers. This chapter covers how an RTL netlist is -transformed into a functionally equivialent netlist utililizing the cell types +transformed into a functionally equivalent netlist utilizing the cell types available in the target architecture. Technology mapping is often performed in two phases. In the first phase RTL cells diff --git a/manual/CHAPTER_Verilog.tex b/manual/CHAPTER_Verilog.tex index 485b4f357..e9ca6114e 100644 --- a/manual/CHAPTER_Verilog.tex +++ b/manual/CHAPTER_Verilog.tex @@ -98,7 +98,7 @@ The lexer does little more than identifying all keywords and literals recognised by the Yosys Verilog frontend. \end{sloppypar} -The lexer keeps track of the current location in the verilog source code using +The lexer keeps track of the current location in the Verilog source code using some global variables. These variables are used by the constructor of AST nodes to annotate each node with the source code location it originated from. @@ -168,11 +168,11 @@ Created by the simplifier when an undeclared signal name is used. \\ \hline % {\tt AST\_PARASET} & -Parameter set in cell instanciation \\ +Parameter set in cell instantiation \\ \hline % {\tt AST\_ARGUMENT} & -Port connection in cell instanciation \\ +Port connection in cell instantiation \\ \hline % {\tt AST\_RANGE} & @@ -184,7 +184,7 @@ A literal value \\ \hline % {\tt AST\_CELLTYPE} & -The type of cell in cell instanciation \\ +The type of cell in cell instantiation \\ \hline % {\tt AST\_IDENTIFIER} & @@ -251,8 +251,8 @@ The unary reduction operators \break \hline % {\tt AST\_REDUCE\_BOOL} & -Conversion from multi-bit value to boolian value -(equivialent to {\tt AST\_REDUCE\_OR}) \\ +Conversion from multi-bit value to boolean value +(equivalent to {\tt AST\_REDUCE\_OR}) \\ \hline % {\tt AST\_SHIFT\_LEFT}, @@ -327,7 +327,7 @@ An \lstinline[language=Verilog];assign; statement \\ \hline % {\tt AST\_CELL} & -A cell instanciation \\ +A cell instantiation \\ \hline % {\tt AST\_PRIMITIVE} & @@ -359,7 +359,7 @@ and the default case respectively \\ \hline % {\tt AST\_FOR} & -A \lstinline[language=Verilog];for;-loop witn an +A \lstinline[language=Verilog];for;-loop with an \lstinline[language=Verilog];always;- or \lstinline[language=Verilog];initial;-block \\ \hline @@ -470,7 +470,7 @@ This produces an AST that is fairly easy to convert to the RTLIL format. \subsection{Generating RTLIL} After AST simplification, the \lstinline[language=C++]{AST::AstNode::genRTLIL()} method of each {\tt AST\_MODULE} node -in the AST is called. This initiates a recursive process that generates equivialent RTLIL data for the AST data. +in the AST is called. This initiates a recursive process that generates equivalent RTLIL data for the AST data. The \lstinline[language=C++]{AST::AstNode::genRTLIL()} method returns an \lstinline[language=C++]{RTLIL::SigSpec} structure. For nodes that represent expressions (operators, constants, signals, etc.), the cells needed to implement the calculation @@ -550,23 +550,23 @@ process $proc$<input>:1$1 switch \in2 case 1'1 assign $1\out1[0:0] $logic_not$<input>:4$2_Y - case + case assign $1\out1[0:0] \in1 end switch \in3 case 1'1 assign $0\out2[0:0] \out2 - case + case end switch \in4 case 1'1 switch \in5 case 1'1 assign $0\out3[0:0] \in6 - case + case assign $0\out3[0:0] \in7 end - case + case end sync posedge \clock update \out1 $0\out1[0:0] @@ -641,7 +641,7 @@ A pointer to a \lstinline[language=C++]{RTLIL::CaseRule} object. Initially this generated \lstinline[language=C++]{RTLIL::Process}. \end{itemize} -As the algorithm runs these variables are continously modified as well as pushed +As the algorithm runs these variables are continuously modified as well as pushed to the stack and later restored to their earlier values by popping from the stack. On startup the ProcessGenerator generates a new @@ -703,7 +703,7 @@ the ProcessGenerator: \item A new \lstinline[language=C++]{RTLIL::SwitchRule} object is generated, the selection expression is evaluated using \lstinline[language=C++]{AST::AstNode::genRTLIL()} (with the use of \lstinline[language=C++]{subst_rvalue_from} and \lstinline[language=C++]{subst_rvalue_to}) and added to the \lstinline[language=C++]{RTLIL::SwitchRule} object and the -obect is added to the \lstinline[language=C++]{current_case}. +object is added to the \lstinline[language=C++]{current_case}. % \item All lvalues assigned to within the {\tt AST\_CASE} node using blocking assignments are collected and saved in the local variable \lstinline[language=C++]{this_case_eq_lvalue}. @@ -837,7 +837,7 @@ as sr-latches or d-latches, without having to extend the actual Verilog frontend \begin{fixme} Add some information on the generation of {\tt \$memrd} and {\tt \$memwr} cells -and how they are processsed in the {\tt memory} pass. +and how they are processed in the {\tt memory} pass. \end{fixme} \section{Synthesizing Parametric Designs} diff --git a/manual/PRESENTATION_ExAdv.tex b/manual/PRESENTATION_ExAdv.tex index 743500918..ef8f64cec 100644 --- a/manual/PRESENTATION_ExAdv.tex +++ b/manual/PRESENTATION_ExAdv.tex @@ -245,7 +245,7 @@ show -color red @cone_ab -color magenta @cone_a -color blue @cone_b \begin{itemize} \item The {\tt techmap} command replaces cells in the design with implementations given -as verilog code (called ``map files''). It can replace Yosys' internal cell +as Verilog code (called ``map files''). It can replace Yosys' internal cell types (such as {\tt \$or}) as well as user-defined cell types. \medskip\item Verilog parameters are used extensively to customize the internal cell types. @@ -480,7 +480,7 @@ cells in ASICS or dedicated carry logic in FPGAs. \subsubsection{Intro to coarse-grain synthesis} \begin{frame}[fragile]{\subsubsecname} -In coarse-grain synthesis the target architecure has cells of the same +In coarse-grain synthesis the target architecture has cells of the same complexity or larger complexity than the internal RTL representation. For example: @@ -558,7 +558,7 @@ $\downarrow$ & $\downarrow$ \\ \begin{frame}{\subsubsecname} \scriptsize Often a coarse-grain element has a constant bit-width, but can be used to -implement oprations with a smaller bit-width. For example, a 18x25-bit multiplier +implement operations with a smaller bit-width. For example, a 18x25-bit multiplier can also be used to implement 16x20-bit multiplication. \bigskip @@ -821,7 +821,7 @@ scripts as well as in reverse engineering and analysis. \item {\bf Behavioral changes} \\ Commands such as {\tt techmap} can be used to make behavioral changes to the design, for example -changing asynchonous resets to synchronous resets. This has applications in design space exploration +changing asynchronous resets to synchronous resets. This has applications in design space exploration (evaluation of various architectures for one circuit). \end{itemize} \end{frame} @@ -844,13 +844,13 @@ module adff2dff (CLK, ARST, D, Q); parameter CLK_POLARITY = 1; parameter ARST_POLARITY = 1; parameter ARST_VALUE = 0; - + input CLK, ARST; input [WIDTH-1:0] D; output reg [WIDTH-1:0] Q; - + wire [1023:0] _TECHMAP_DO_ = "proc"; - + wire _TECHMAP_FAIL_ = !CLK_POLARITY || !ARST_POLARITY; \end{lstlisting} \vss} @@ -877,7 +877,7 @@ endmodule \begin{frame}{\subsecname} \begin{itemize} -\item A lot can be achived in Yosys just with the standard set of commands. +\item A lot can be achieved in Yosys just with the standard set of commands. \item The commands {\tt techmap} and {\tt extract} can be used to prototype many complex synthesis tasks. \end{itemize} diff --git a/manual/PRESENTATION_ExAdv/addshift_map.v b/manual/PRESENTATION_ExAdv/addshift_map.v index b6d91b01b..13ecf0bae 100644 --- a/manual/PRESENTATION_ExAdv/addshift_map.v +++ b/manual/PRESENTATION_ExAdv/addshift_map.v @@ -4,17 +4,17 @@ module \$add (A, B, Y); parameter A_WIDTH = 1; parameter B_WIDTH = 1; parameter Y_WIDTH = 1; - + input [A_WIDTH-1:0] A; input [B_WIDTH-1:0] B; output [Y_WIDTH-1:0] Y; - + parameter _TECHMAP_BITS_CONNMAP_ = 0; parameter _TECHMAP_CONNMAP_A_ = 0; parameter _TECHMAP_CONNMAP_B_ = 0; - + wire _TECHMAP_FAIL_ = A_WIDTH != B_WIDTH || B_WIDTH < Y_WIDTH || _TECHMAP_CONNMAP_A_ != _TECHMAP_CONNMAP_B_; - + assign Y = A << 1; endmodule diff --git a/manual/PRESENTATION_ExAdv/red_or3x1_map.v b/manual/PRESENTATION_ExAdv/red_or3x1_map.v index 24ca9dab4..8c37b1dba 100644 --- a/manual/PRESENTATION_ExAdv/red_or3x1_map.v +++ b/manual/PRESENTATION_ExAdv/red_or3x1_map.v @@ -3,10 +3,10 @@ module \$reduce_or (A, Y); parameter A_SIGNED = 0; parameter A_WIDTH = 0; parameter Y_WIDTH = 0; - + input [A_WIDTH-1:0] A; output [Y_WIDTH-1:0] Y; - + function integer min; input integer a, b; begin @@ -16,7 +16,7 @@ module \$reduce_or (A, Y); min = b; end endfunction - + genvar i; generate begin if (A_WIDTH == 0) begin diff --git a/manual/PRESENTATION_ExAdv/sym_mul_map.v b/manual/PRESENTATION_ExAdv/sym_mul_map.v index 293c5b841..b4dbd9e07 100644 --- a/manual/PRESENTATION_ExAdv/sym_mul_map.v +++ b/manual/PRESENTATION_ExAdv/sym_mul_map.v @@ -4,12 +4,12 @@ module \$mul (A, B, Y); parameter A_WIDTH = 1; parameter B_WIDTH = 1; parameter Y_WIDTH = 1; - + input [A_WIDTH-1:0] A; input [B_WIDTH-1:0] B; output [Y_WIDTH-1:0] Y; - + wire _TECHMAP_FAIL_ = A_WIDTH != B_WIDTH || B_WIDTH != Y_WIDTH; - + MYMUL #( .WIDTH(Y_WIDTH) ) g ( .A(A), .B(B), .Y(Y) ); endmodule diff --git a/manual/PRESENTATION_ExOth.tex b/manual/PRESENTATION_ExOth.tex index f86dcd7ac..73f8bea2e 100644 --- a/manual/PRESENTATION_ExOth.tex +++ b/manual/PRESENTATION_ExOth.tex @@ -33,8 +33,8 @@ as {\tt \%ci} and {\tt \%co}, can be used to figure out how parts of the design are connected. \item -Commands such as {\tt submod}, {\tt expose}, {\tt splice}, \dots can be used -to transform the design into an equivialent design that is easier to analyse. +Commands such as {\tt submod}, {\tt expose}, {\tt splice}, \dots can be used +to transform the design into an equivalent design that is easier to analyse. \item Commands such as {\tt eval} and {\tt sat} can be used to investigate the @@ -102,7 +102,7 @@ Symbolic Model Checking (SMC) is used to formally prove that a circuit has (or has not) a given property. \bigskip -One appliction is Formal Equivalence Checking: Proving that two circuits +One application is Formal Equivalence Checking: Proving that two circuits are identical. For example this is a very useful feature when debugging custom passes in Yosys. @@ -115,7 +115,7 @@ The {\tt sat} command in Yosys can be used to perform Symbolic Model Checking. \end{frame} \begin{frame}[t]{Example: Formal Equivalence Checking (1/2)} -Remember the following example? +Remember the following example? \vskip1em \vbox to 0cm{ @@ -143,11 +143,11 @@ rename test test_mapped # apply the techmap only to test_mapped techmap -map techmap_01_map.v test_mapped -# create a miter circuit to test equivialence +# create a miter circuit to test equivalence miter -equiv -make_assert -make_outputs test_orig test_mapped miter flatten miter -# run equivialence check +# run equivalence check sat -verify -prove-asserts -show-inputs -show-outputs miter \end{lstlisting} diff --git a/manual/PRESENTATION_ExOth/equiv.ys b/manual/PRESENTATION_ExOth/equiv.ys index 09a4045db..8db0a88a5 100644 --- a/manual/PRESENTATION_ExOth/equiv.ys +++ b/manual/PRESENTATION_ExOth/equiv.ys @@ -9,9 +9,9 @@ rename test test_mapped # apply the techmap only to test_mapped techmap -map ../PRESENTATION_ExSyn/techmap_01_map.v test_mapped -# create a miter circuit to test equivialence +# create a miter circuit to test equivalence miter -equiv -make_assert -make_outputs test_orig test_mapped miter flatten miter -# run equivialence check +# run equivalence check sat -verify -prove-asserts -show-inputs -show-outputs miter diff --git a/manual/PRESENTATION_ExSyn.tex b/manual/PRESENTATION_ExSyn.tex index b7d6b8a6d..655720ebc 100644 --- a/manual/PRESENTATION_ExSyn.tex +++ b/manual/PRESENTATION_ExSyn.tex @@ -22,7 +22,7 @@ \item Convert remaining logic to bit-level logic functions \item Perform optimizations on bit-level logic functions \item Map bit-level logic gates and registers to cell library -\item Write results to output file +\item Write results to output file \end{itemize} \end{frame} @@ -144,16 +144,16 @@ The {\tt opt} command implements a series of simple optimizations. It also is a macro command that calls other commands: \begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -opt_const # const folding -opt_share -nomux # merging identical cells +opt_expr # const folding and simple expression rewriting +opt_merge -nomux # merging identical cells do opt_muxtree # remove never-active branches from multiplexer tree opt_reduce # consolidate trees of boolean ops to reduce functions - opt_share # merging identical cells + opt_merge # merging identical cells opt_rmdff # remove/simplify registers with constant inputs opt_clean # remove unused objects (cells, wires) from design - opt_const # const folding + opt_expr # const folding and simple expression rewriting while [changed design] \end{lstlisting} @@ -161,7 +161,7 @@ The command {\tt clean} can be used as alias for {\tt opt\_clean}. And {\tt ;;} can be used as shortcut for {\tt clean}. For example: \begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -proc; opt; memory; opt_const;; fsm;; +proc; opt; memory; opt_expr;; fsm;; \end{lstlisting} \end{frame} @@ -216,7 +216,7 @@ proc; opt; memory; opt_const;; fsm;; \begin{frame}{\subsecname} Usually it does not hurt to call {\tt opt} after each regular command in the synthesis script. But it increases the synthesis time, so it is favourable -to only call {\tt opt} when an improvement can be archieved. +to only call {\tt opt} when an improvement can be achieved. \bigskip The designs in {\tt yosys-bigsim} are a good playground for experimenting with @@ -320,7 +320,7 @@ fsm_map # unless got option -nomap \end{frame} \begin{frame}{\subsecname{} -- details} -Some details on the most importand commands from the {\tt fsm\_*} group: +Some details on the most important commands from the {\tt fsm\_*} group: \bigskip The {\tt fsm\_detect} command identifies FSM state registers and marks them diff --git a/manual/PRESENTATION_Intro.tex b/manual/PRESENTATION_Intro.tex index 5aeebd9f9..555ec9175 100644 --- a/manual/PRESENTATION_Intro.tex +++ b/manual/PRESENTATION_Intro.tex @@ -31,12 +31,12 @@ \only<3>{Netlists}% \only<4>{Hardware Description Languages (HDLs)}} \only<1>{ - Graphical representation of the circtuit topology. Circuit elements - are represented by symbols and electrical connections by lines. The gometric + Graphical representation of the circuit topology. Circuit elements + are represented by symbols and electrical connections by lines. The geometric layout is for readability only. }% \only<2>{ - The actual physical geometry of the device (PCB or ASIC manufracturing masks). + The actual physical geometry of the device (PCB or ASIC manufacturing masks). This is the final product of the design process. }% \only<3>{ @@ -86,7 +86,7 @@ }% \only<4>{ List of registers (flip-flops) and logic functions that calculate the next state from the previous one. Usually - a netlist utilizing high-level cells such as adders, multiplieres, multiplexer, etc. + a netlist utilizing high-level cells such as adders, multipliers, multiplexer, etc. }% \only<5>{ Netlist of single-bit registers and basic logic gates (such as AND, OR, @@ -95,7 +95,7 @@ }% \only<6>{ Netlist of cells that actually are available on the target architecture - (such as CMOS gates in an ASCI or LUTs in an FPGA). Optimized for + (such as CMOS gates in an ASIC or LUTs in an FPGA). Optimized for area, power, and/or speed (static timing or number of logic levels). }% \only<7>{ @@ -155,7 +155,7 @@ Things Yosys can do: \begin{itemize} \item Read and process (most of) modern Verilog-2005 code. \item Perform all kinds of operations on netlist (RTL, Logic, Gate). -\item Perform logic optimiziations and gate mapping with ABC\footnote[frame]{\url{http://www.eecs.berkeley.edu/~alanmi/abc/}}. +\item Perform logic optimizations and gate mapping with ABC\footnote[frame]{\url{http://www.eecs.berkeley.edu/~alanmi/abc/}}. \end{itemize} \bigskip @@ -176,7 +176,7 @@ as Qflow\footnote[frame]{\url{http://opencircuitdesign.com/qflow/}} for ASIC des \subsection{Yosys Data- and Control-Flow} \begin{frame}{\subsecname} - A (usually short) synthesis script controlls Yosys. + A (usually short) synthesis script controls Yosys. This scripts contain three types of commands: \begin{itemize} @@ -503,7 +503,7 @@ Commands for executing scripts or entering interactive mode: Commands for reading and elaborating the design: \begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] read_ilang # read modules from ilang file - read_verilog # read modules from verilog file + read_verilog # read modules from Verilog file hierarchy # check, expand and clean up design hierarchy \end{lstlisting} @@ -536,7 +536,7 @@ Commands for writing the results: write_edif # write design to EDIF netlist file write_ilang # write design to ilang file write_spice # write design to SPICE netlist file - write_verilog # write design to verilog file + write_verilog # write design to Verilog file \end{lstlisting} \bigskip @@ -658,7 +658,7 @@ endmodule \subsection{Verification of Yosys} \begin{frame}{\subsecname} -Contiously checking the correctness of Yosys and making sure that new features +Continuously checking the correctness of Yosys and making sure that new features do not break old ones is a high priority in Yosys. \bigskip @@ -697,7 +697,7 @@ the other tools used as external reference where found and reported so far. \begin{frame}{\subsecname{} -- yosys-bigsim} yosys-bigsim is a collection of real-world open-source Verilog designs and test -benches. yosys-bigsim compares the testbench outpus of simulations of the original +benches. yosys-bigsim compares the testbench outputs of simulations of the original Verilog code and synthesis results. \bigskip @@ -721,7 +721,7 @@ The following designs are included in yosys-bigsim (excerpt): \begin{frame}{\subsecname} \begin{itemize} \item Cost (also applies to ``free as in free beer'' solutions) -\item Availablity and Reproducability +\item Availability and Reproducibility \item Framework- and all-in-one-aspects \item Educational Tool \end{itemize} @@ -739,7 +739,7 @@ the cost for the design tools needed to design the mask layouts. Open Source ASIC flows are an important enabler for ASIC-level Open Source Hardware. \bigskip -\item Availablity and Reproducability: \smallskip\par +\item Availability and Reproducibility: \smallskip\par If you are a researcher who is publishing, you want to use tools that everyone else can also use. Even if most universities have access to all major commercial tools, you usually do not have easy access to the version that was @@ -757,18 +757,18 @@ basic functionality. Extensibility was one of Yosys' design goals. \bigskip \item All-in-one: \smallskip\par -Because of the framework characterisitcs of Yosys, an increasing number of features +Because of the framework characteristics of Yosys, an increasing number of features become available in one tool. Yosys not only can be used for circuit synthesis but -also for formal equivialence checking, SAT solving, and for circuit analysis, to +also for formal equivalence checking, SAT solving, and for circuit analysis, to name just a few other application domains. With proprietary software one needs to -learn a new tool for each of this applications. +learn a new tool for each of these applications. \end{itemize} \end{frame} \begin{frame}{\subsecname{} -- 3/3} \begin{itemize} \item Educational Tool: \smallskip\par -Propritaery synthesis tools are at times very secretive about their inner +Proprietary synthesis tools are at times very secretive about their inner workings. They often are ``black boxes''. Yosys is very open about its internals and it is easy to observe the different steps of synthesis. \end{itemize} @@ -789,8 +789,8 @@ copyright notice and this permission notice appear in all copies. \begin{itemize} \item Synthesis of final production designs \item Pre-production synthesis (trial runs before investing in other tools) -\item Convertion of full-featured Verilog to simple Verilog -\item Convertion of Verilog to other formats (BLIF, BTOR, etc) +\item Conversion of full-featured Verilog to simple Verilog +\item Conversion of Verilog to other formats (BLIF, BTOR, etc) \item Demonstrating synthesis algorithms (e.g. for educational purposes) \item Framework for experimenting with new algorithms \item Framework for building custom flows\footnote[frame]{Not limited to synthesis @@ -908,7 +908,7 @@ control logic because it is simpler than setting up a commercial flow. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\subsection{Documentation, Downloads, Contatcs} +\subsection{Documentation, Downloads, Contacts} \begin{frame}{\subsecname} \begin{itemize} @@ -916,7 +916,7 @@ control logic because it is simpler than setting up a commercial flow. \smallskip\hskip1cm\url{http://www.clifford.at/yosys/} \bigskip -\item Manual, Command Reference, Appliction Notes: \\ +\item Manual, Command Reference, Application Notes: \\ \smallskip\hskip1cm\url{http://www.clifford.at/yosys/documentation.html} \bigskip diff --git a/manual/PRESENTATION_Intro/counter.ys b/manual/PRESENTATION_Intro/counter.ys index 8b3390ed4..cc4e7cd31 100644 --- a/manual/PRESENTATION_Intro/counter.ys +++ b/manual/PRESENTATION_Intro/counter.ys @@ -1,4 +1,4 @@ -# read design +# read design read_verilog counter.v hierarchy -check -top counter diff --git a/manual/PRESENTATION_Prog.tex b/manual/PRESENTATION_Prog.tex index 96189e55f..b85eda892 100644 --- a/manual/PRESENTATION_Prog.tex +++ b/manual/PRESENTATION_Prog.tex @@ -124,7 +124,7 @@ has been executed. \begin{frame}{\subsecname} The RTLIL data structures are simple structs utilizing {\tt pool<>} and -{\tt dict<>} containers (drop-in replacementments for {\tt +{\tt dict<>} containers (drop-in replacements for {\tt std::unordered\_set<>} and {\tt std::unordered\_map<>}). \bigskip @@ -325,7 +325,7 @@ Simulation models (i.e. {\it documentation\/} :-) for the internal cell library: \bigskip The lower-case cell types (such as {\tt \$and}) are parameterized cells of variable -width. This so-called {\it RTL Cells\/} are the cells described in {\tt simlib.v}. +width. This so-called {\it RTL Cells\/} are the cells described in {\tt simlib.v}. \bigskip The upper-case cell types (such as {\tt \$\_AND\_}) are single-bit cells that are not @@ -413,7 +413,7 @@ When modifying existing modules, stick to the following DOs and DON'Ts: \item Use {\tt module->fixup\_ports()} after changing the {\tt port\_*} properties of wires. -\item You can safely remove cells or change the {\tt connetions} property of a cell, but be careful when +\item You can safely remove cells or change the {\tt connections} property of a cell, but be careful when changing the size of the {\tt SigSpec} connected to a cell port. \item Use the {\tt SigMap} helper class (see next slide) when you need a unique handle for each signal bit. @@ -477,7 +477,7 @@ log("Name of this module: %s\n", log_id(module->name)); \medskip Use {\tt log\_header()} and {\tt log\_push()}/{\tt log\_pop()} to structure log messages: \begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -log_header("Doing important stuff!\n"); +log_header(design, "Doing important stuff!\n"); log_push(); for (int i = 0; i < 10; i++) log("Log message #%d.\n", i); @@ -534,7 +534,7 @@ struct MyPass : public Pass { log("Modules in current design:\n"); for (auto mod : design->modules()) log(" %s (%d wires, %d cells)\n", log_id(mod), - GetSize(mod->wires), GetSize(mod->cells)); + GetSize(mod->wires()), GetSize(mod->cells())); } } MyPass; \end{lstlisting} diff --git a/manual/PRESENTATION_Prog/my_cmd.cc b/manual/PRESENTATION_Prog/my_cmd.cc index 1d28ce974..d99bfe1e8 100644 --- a/manual/PRESENTATION_Prog/my_cmd.cc +++ b/manual/PRESENTATION_Prog/my_cmd.cc @@ -65,7 +65,7 @@ struct Test2Pass : public Pass { log("Mapped signal x: %s\n", log_signal(sigmap(x))); - log_header("Doing important stuff!\n"); + log_header(design, "Doing important stuff!\n"); log_push(); for (int i = 0; i < 10; i++) log("Log message #%d.\n", i); diff --git a/manual/appnotes.sh b/manual/appnotes.sh index 1160af6af..0ae52862e 100755 --- a/manual/appnotes.sh +++ b/manual/appnotes.sh @@ -1,7 +1,7 @@ #!/bin/bash set -ex -for job in APPNOTE_010_Verilog_to_BLIF APPNOTE_011_Design_Investigation +for job in APPNOTE_010_Verilog_to_BLIF APPNOTE_011_Design_Investigation APPNOTE_012_Verilog_to_BTOR do [ -f $job.ok -a $job.ok -nt $job.tex ] && continue if [ -f $job/make.sh ]; then diff --git a/manual/clean.sh b/manual/clean.sh index f4a2ea83a..11c2e7bf2 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 047ec4214..425d89b67 100644 --- a/manual/command-reference-manual.tex +++ b/manual/command-reference-manual.tex @@ -16,7 +16,7 @@ library to a target architecture. use the specified ABC script file instead of the default script. if <file> starts with a plus sign (+), then the rest of the filename - string is interprated as the command string to be passed to ABC. the + string is interpreted as the command string to be passed to ABC. The leading plus sign is removed and all commas (,) in the string are replaced with blanks before the string is passed to ABC. @@ -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 @@ -90,7 +99,7 @@ library to a target architecture. -keepff set the "keep" attribute on flip-flop output wires. (and thus preserve - them, for example for equivialence checking.) + them, for example for equivalence checking.) -nocleanup when this option is used, the temporary files created by this pass @@ -136,6 +145,18 @@ Like 'add -input', but also connect the signal between instances of the selected modules. \end{lstlisting} +\section{aigmap -- map logic to and-inverter-graph circuit} +\label{cmd:aigmap} +\begin{lstlisting}[numbers=left,frame=single] + aigmap [options] [selection] + +Replace all logic cells with circuits made of only $_AND_ and +$_NOT_ cells. + + -nand + Enable creation of $_NAND_ cells +\end{lstlisting} + \section{alumacc -- extract ALU and MACC cells} \label{cmd:alumacc} \begin{lstlisting}[numbers=left,frame=single] @@ -156,7 +177,7 @@ This is just a shortcut for 'select -module <modname>'. cd <cellname> When no module with the specified name is found, but there is a cell -with the specified name in the current module, then this is equivialent +with the specified name in the current module, then this is equivalent to 'cd <celltype>'. cd .. @@ -164,6 +185,40 @@ to 'cd <celltype>'. This is just a shortcut for 'select -clear'. \end{lstlisting} +\section{check -- check for obvious problems in the design} +\label{cmd:check} +\begin{lstlisting}[numbers=left,frame=single] + check [options] [selection] + +This pass identifies the following problems in the current design: + + - combinatorial loops + + - two or more conflicting drivers for one wire + + - used wires that do not have a driver + +When called with -noinit then this command also checks for wires which have +the 'init' attribute set. + +When called with -assert then the command will produce an error if any +problems are found in the current design. +\end{lstlisting} + +\section{chparam -- re-evaluate modules with new parameters} +\label{cmd:chparam} +\begin{lstlisting}[numbers=left,frame=single] + chparam [ -set name value ]... [selection] + +Re-evaluate the selected modules with new parameters. String values must be +passed in double quotes ("). + + + chparam -list [selection] + +List the available parameters of the selected modules. +\end{lstlisting} + \section{clean -- remove unused cells and wires} \label{cmd:clean} \begin{lstlisting}[numbers=left,frame=single] @@ -183,8 +238,8 @@ in -purge mode between the commands. \begin{lstlisting}[numbers=left,frame=single] connect [-nomap] [-nounset] -set <lhs-expr> <rhs-expr> -Create a connection. This is equivialent to adding the statement 'assign -<lhs-expr> = <rhs-expr>;' to the verilog input. Per default, all existing +Create a connection. This is equivalent to adding the statement 'assign +<lhs-expr> = <rhs-expr>;' to the Verilog input. Per default, all existing drivers for <lhs-expr> are unconnected. This can be overwritten by using the -nounset option. @@ -216,8 +271,8 @@ This command does not operate on module with processes. Wrappers are used in coarse-grain synthesis to wrap cells with smaller ports in wrapper cells with a (larger) constant port size. I.e. the upper bits -of the wrapper outut are signed/unsigned bit extended. This command uses this -knowlege to rewire the inputs of the driven cells to match the output of +of the wrapper output are signed/unsigned bit extended. This command uses this +knowledge to rewire the inputs of the driven cells to match the output of the driving cell. -signed <cell_type> <port_name> <width_param> @@ -343,13 +398,13 @@ evaluated in the other design. design -copy-to <name> [-as <new_mod_name>] [selection] -Copy modules from the current design into the soecified one. +Copy modules from the current design into the specified one. \end{lstlisting} -\section{dff2dffe -- transform $dff cells to $dffe cells} +\section{dff2dffe -- transform \$dff cells to \$dffe cells} \label{cmd:dff2dffe} \begin{lstlisting}[numbers=left,frame=single] - dff2dffe [selection] + dff2dffe [options] [selection] This pass transforms $dff cells driven by a tree of multiplexers with one or more feedback paths to $dffe cells. It also works on gate-level cells such as @@ -365,8 +420,27 @@ $_DFF_P_, $_DFF_N_ and $_MUX_. <external_gate_type> is the cell type name for a cell with an identical interface to the <internal_gate_type>, except it also has an high-active enable port 'E'. - Usually <external_gate_type> is an intemediate cell type + Usually <external_gate_type> is an intermediate cell type that is then translated to the final type using 'techmap'. + + -direct-match <pattern> + like -direct for all DFF cell types matching the expression. + this will use $__DFFE_* as <external_gate_type> matching the + internal gate type $_DFF_*_, except for $_DFF_[NP]_, which is + converted to $_DFFE_[NP]_. +\end{lstlisting} + +\section{dffinit -- set INIT param on FF cells} +\label{cmd:dffinit} +\begin{lstlisting}[numbers=left,frame=single] + dffinit [options] [selection] + +This pass sets an FF cell parameter to the the initial value of the net it +drives. (This is primarily used in FPGA flows.) + + -ff <cell_name> <output_port> <init_param> + operate on the specified cell type. this option can be used + multiple times. \end{lstlisting} \section{dfflibmap -- technology mapping of flip-flops} @@ -385,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] @@ -420,15 +503,29 @@ Print all commands to log before executing them. Do not print all commands to log before executing them. (default) \end{lstlisting} -\section{equiv\_add -- add a $equiv cell} +\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} +\section{equiv\_induct -- proving \$equiv cells using temporal induction} \label{cmd:equiv_induct} \begin{lstlisting}[numbers=left,frame=single] equiv_induct [options] [selection] @@ -473,7 +570,7 @@ to work with the created equivalent checking module. Do not match cells or signals that match the names in the file. -encfile <file> - Match FSM encodings using the desiption from the file. + Match FSM encodings using the description from the file. See 'help fsm_recode' for details. Note: The circuit created by this command is not a miter (with something like @@ -481,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] @@ -501,7 +609,17 @@ This creates a miter module for further analysis of the selected $equiv cells. Create compare logic that handles undefs correctly \end{lstlisting} -\section{equiv\_remove -- remove $equiv cells} +\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] equiv_remove [options] [selection] @@ -516,7 +634,7 @@ used then only proven cells are removed. keep gate circuit \end{lstlisting} -\section{equiv\_simple -- try proving simple $equiv instances} +\section{equiv\_simple -- try proving simple \$equiv instances} \label{cmd:equiv_simple} \begin{lstlisting}[numbers=left,frame=single] equiv_simple [options] [selection] @@ -547,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] @@ -585,8 +733,8 @@ outputs. signal path at that wire. -shared - only expose those signals that are shared ammong the selected modules. - this is useful for preparing modules for equivialence checking. + only expose those signals that are shared among the selected modules. + this is useful for preparing modules for equivalence checking. -evert also turn connections to instances of other modules to additional @@ -609,7 +757,7 @@ outputs. This pass looks for subcircuits that are isomorphic to any of the modules in the given map file and replaces them with instances of this modules. The -map file can be a verilog source file (*.v) or an ilang file (*.il). +map file can be a Verilog source file (*.v) or an ilang file (*.il). -map <map_file> use the modules in this file as reference. This option can be used @@ -638,11 +786,11 @@ map file can be a verilog source file (*.v) or an ilang file (*.il). match. This option can be used multiple times. -swap <needle_type> <port1>,<port2>[,...] - Register a set of swapable ports for a needle cell type. + Register a set of swappable ports for a needle cell type. This option can be used multiple times. -perm <needle_type> <port1>,<port2>[,...] <portA>,<portB>[,...] - Register a valid permutation of swapable ports for a needle + Register a valid permutation of swappable ports for a needle cell type. This option can be used multiple times. -cell_attr <attribute_name> @@ -657,7 +805,7 @@ map file can be a verilog source file (*.v) or an ilang file (*.il). -ignore_param <cell_type> <parameter_name> Do not use this parameter when matching cells. -This pass does not operate on modules with uprocessed processes in it. +This pass does not operate on modules with unprocessed processes in it. (I.e. the 'proc' pass should be used first to convert processes to netlists.) This pass can also be used for mining for frequent subcircuits. In this mode @@ -694,8 +842,11 @@ See 'help techmap' for a pass that does the opposite thing. flatten [selection] This pass flattens the design by replacing cells by their implementation. This -pass is very simmilar to the 'techmap' pass. The only difference is that this +pass is very similar to the 'techmap' pass. The only difference is that this pass is using the current design as mapping library. + +Cells and/or modules with the 'keep_hierarchy' attribute set will not be +flattened by this command. \end{lstlisting} \section{freduce -- perform functional reduction} @@ -704,7 +855,7 @@ pass is using the current design as mapping library. freduce [options] [selection] This pass performs functional reduction in the circuit. I.e. if two nodes are -equivialent, they are merged to one node and one of the redundant drivers is +equivalent, they are merged to one node and one of the redundant drivers is disconnected. A subsequent call to 'clean' will remove the redundant drivers. -v, -vv @@ -722,7 +873,7 @@ disconnected. A subsequent call to 'clean' will remove the redundant drivers. operation. this is mostly used for debugging the freduce command. This pass is undef-aware, i.e. it considers don't-care values for detecting -equivialent nodes. +equivalent nodes. All selected wires are considered for rewiring. The selected cells cover the circuit that is analyzed. @@ -734,7 +885,7 @@ circuit that is analyzed. fsm [options] [selection] This pass calls all the other fsm_* passes in a useful order. This performs -FSM extraction and optimiziation. It also calls opt_clean as needed: +FSM extraction and optimization. It also calls opt_clean as needed: fsm_detect unless got option -nodetect fsm_extract @@ -759,7 +910,7 @@ Options: -expand, -norecode, -export, -nomap enable or disable passes as indicated above - -encoding tye + -encoding type -fm_set_fsm_file file -encfile file passed through to fsm_recode pass @@ -787,7 +938,7 @@ Signals can be protected from being detected by this pass by setting the The fsm_extract pass is conservative about the cells that belong to a finite state machine. This pass can be used to merge additional auxiliary gates into -the finate state machine. +the finite state machine. \end{lstlisting} \section{fsm\_export -- exporting FSMs to KISS2 files} @@ -881,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} @@ -903,7 +1058,7 @@ needed. -purge_lib by default the hierarchy command will not remove library (blackbox) - module. use this options to also remove unused blackbox modules. + modules. use this option to also remove unused blackbox modules. -libdir <directory> search for files named <module_name>.v in the specified directory @@ -927,6 +1082,9 @@ needed. specified top module. otherwise a module with the 'top' attribute set will implicitly be used as top module, if such a module exists. + -auto-top + automatically determine the top of the design hierarchy and mark it. + In -generate mode this pass generates blackbox modules for the given cell types (wildcards supported). For this the design is searched for cells that match the given types and then the given port declarations are used to @@ -936,7 +1094,7 @@ determine the direction of the ports. The syntax for a port declaration is: Input ports are specified with the 'i' prefix, output ports with the 'o' prefix and inout ports with the 'io' prefix. The optional <num> specifies -the position of the port in the parameter list (needed when instanciated +the position of the port in the parameter list (needed when instantiated using positional arguments). When <num> is not specified, the <portname> can also contain wildcard characters. @@ -973,6 +1131,39 @@ 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] + ice40_ffssr [options] [selection] + +Merge synchronous set/reset $_MUX_ cells into iCE40 FFs. +\end{lstlisting} + +\section{ice40\_opt -- iCE40: perform simple optimizations} +\label{cmd:ice40_opt} +\begin{lstlisting}[numbers=left,frame=single] + ice40_opt [options] [selection] + +This command executes the following script: + + do + <ice40 specific optimizations> + opt_const -mux_undef -undriven [-full] + opt_share + opt_rmdff + opt_clean + while <changed design> +\end{lstlisting} + \section{iopadmap -- technology mapping of i/o pads (or buffers)} \label{cmd:iopadmap} \begin{lstlisting}[numbers=left,frame=single] @@ -983,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>] @@ -1004,6 +1195,22 @@ the resulting cells to more sophisticated PAD cells. buffers using -widthparam to set the word size on the cell.) \end{lstlisting} +\section{json -- write design in JSON format} +\label{cmd:json} +\begin{lstlisting}[numbers=left,frame=single] + json [options] [selection] + +Write a JSON netlist of all selected objects. + + -o <filename> + write to the specified file. + + -aig + also include AIG models for the different gate types + +See 'help write_json' for a description of the JSON format used. +\end{lstlisting} + \section{log -- print text and log files} \label{cmd:log} \begin{lstlisting}[numbers=left,frame=single] @@ -1038,23 +1245,31 @@ 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] maccmap [-unmap] [selection] -This pass maps $macc cells to yosys gate primitives. When the -unmap option is -used then the $macc cell is mapped to $and, $sub, etc. cells instead. +This pass maps $macc cells to yosys $fa and $alu cells. When the -unmap option +is used then the $macc cell is mapped to $add, $sub, etc. cells instead. \end{lstlisting} \section{memory -- translate memories to basic cells} \label{cmd:memory} \begin{lstlisting}[numbers=left,frame=single] - memory [-nomap] [-bram <bram_rules>] [selection] + memory [-nomap] [-nordff] [-bram <bram_rules>] [selection] This pass calls all the other memory_* passes in a useful order: - memory_dff + memory_dff [-nordff] opt_clean memory_share opt_clean @@ -1079,13 +1294,14 @@ The rules file contains a set of block ram description and a sequence of match rules. A block ram description looks like this: bram RAMB1024X32 # name of BRAM cell + init 1 # set to '1' if BRAM can be initialized abits 10 # number of address bits dbits 32 # number of data bits 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) - transp 0 2 # transparatent (for read 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 endbram @@ -1103,7 +1319,7 @@ and a value greater than 1 means configurable. All groups with the same value greater than 1 share the same configuration bit. Using the same bram name in different bram blocks will create different variants -of the bram. Verilog configration parameters for the bram are created as needed. +of the bram. Verilog configuration parameters for the bram are created as needed. It is also possible to create variants by repeating statements in the bram block and appending '@<label>' to the individual statements. @@ -1136,7 +1352,7 @@ It is possible to match against the following values with min/max rules: dcells ....... number of cells in 'data-direction' cells ........ total number of cells (acells*dcells*dups) -The interface for the created bram instances is dervived from the bram +The interface for the created bram instances is derived from the bram description. Use 'techmap' to convert the created bram instances into instances of the actual bram cells of your target architecture. @@ -1147,6 +1363,9 @@ the next also has 'or_next_if_better' set, and so forth). A match containing the command 'make_transp' will add external circuitry to simulate 'transparent read', if necessary. +A match containing the command 'make_outreg' will add external flip-flops +to implement synchronous read ports, if necessary. + A match containing the command 'shuffle_enable A' will re-organize the data bits to accommodate the enable pattern of port A. \end{lstlisting} @@ -1169,7 +1388,7 @@ This pass detects DFFs at memory ports and merges them into the memory port. I.e. it consumes an asynchronous memory port and the flip-flops at its interface and yields a synchronous memory port. - -wr_only + -nordfff do not merge registers on read ports \end{lstlisting} @@ -1221,7 +1440,7 @@ $memwr cells. It is the counterpart to the memory_collect pass. \begin{lstlisting}[numbers=left,frame=single] miter -equiv [options] gold_name gate_name miter_name -Creates a miter circuit for equivialence checking. The gold- and gate- modules +Creates a miter circuit for equivalence checking. The gold- and gate- modules must have the same interfaces. The miter circuit will have all inputs of the two source modules, prefixed with 'in_'. The miter circuit has a 'trigger' output that goes high if an output mismatch between the two source modules is @@ -1243,6 +1462,53 @@ detected. -flatten call 'flatten; opt_const -keepdc -undriven;;' on the miter circuit. + + + miter -assert [options] module [miter_name] + +Creates a miter circuit for property checking. All input ports are kept, +output ports are discarded. An additional output 'trigger' is created that +goes high when an assert is violated. Without a miter_name, the existing +module is modified. + + -make_outputs + keep module output ports. + + -flatten + call 'flatten; opt_const -keepdc -undriven;;' on the miter circuit. +\end{lstlisting} + +\section{muxcover -- cover trees of MUX cells with wider MUXes} +\label{cmd:muxcover} +\begin{lstlisting}[numbers=left,frame=single] + muxcover [options] [selection] + +Cover trees of $_MUX_ cells with $_MUX{4,8,16}_ cells + + -mux4, -mux8, -mux16 + Use the specified types of MUXes. If none of those options are used, + the effect is the same as if all of them where used. + + -nodecode + Do not insert decoder logic. This reduces the number of possible + substitutions, but guarantees that the resulting circuit is not + 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} @@ -1254,23 +1520,23 @@ This pass calls all the other opt_* passes in a useful order. This performs a series of trivial optimizations and cleanups. This pass executes the other passes in the following order: - opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc] - opt_share -nomux + opt_const [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc] + opt_share [-share_all] -nomux do opt_muxtree opt_reduce [-fine] [-full] - opt_share + opt_share [-share_all] opt_rmdff opt_clean [-purge] - opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc] + opt_const [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc] while <changed design> When called with -fast the following script is used instead: do - opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc] - opt_share + opt_const [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc] + opt_share [-share_all] opt_rmdff opt_clean [-purge] while <changed design in opt_rmdff> @@ -1311,6 +1577,9 @@ This pass performs const folding on internal cell types with constant inputs. -undriven replace undriven nets with undef (x) constants + -clkinv + optimize clock inverters by changing FF types + -fine perform fine-grain optimizations @@ -1368,13 +1637,16 @@ a constant driver. \section{opt\_share -- consolidate identical cells} \label{cmd:opt_share} \begin{lstlisting}[numbers=left,frame=single] - opt_share [-nomux] [selection] + opt_share [options] [selection] This pass identifies cells with identical type and input signals. Such cells are then merged to one cell. -nomux Do not merge MUX cells. + + -share_all + Operate on all cell types, not just built-in types. \end{lstlisting} \section{plugin -- load and list loaded plugins} @@ -1394,6 +1666,57 @@ Load and list loaded plugins. List loaded plugins \end{lstlisting} +\section{pmuxtree -- transform \$pmux cells to trees of \$mux cells} +\label{cmd:pmuxtree} +\begin{lstlisting}[numbers=left,frame=single] + pmuxtree [options] [selection] + +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] @@ -1406,10 +1729,12 @@ This pass calls all the other proc_* passes in the most common order. proc_init proc_arst proc_mux + proc_dlatch proc_dff proc_clean -This replaces the processes in the design with multiplexers and flip-flops. +This replaces the processes in the design with multiplexers, +flip-flops and latches. The following options are supported: @@ -1452,12 +1777,21 @@ This pass identifies flip-flops in the processes and converts them to d-type flip-flop cells. \end{lstlisting} +\section{proc\_dlatch -- extract latches from processes} +\label{cmd:proc_dlatch} +\begin{lstlisting}[numbers=left,frame=single] + proc_dlatch [selection] + +This pass identifies latches in the processes and converts them to +d-type latches. +\end{lstlisting} + \section{proc\_init -- convert initial block to init attributes} \label{cmd:proc_init} \begin{lstlisting}[numbers=left,frame=single] proc_init [selection] -This pass extracts the 'init' actions from processes (generated from verilog +This pass extracts the 'init' actions from processes (generated from Verilog 'initial' blocks) and sets the initial value to the 'init' attribute on the respective wire. \end{lstlisting} @@ -1479,6 +1813,40 @@ 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] + read_blif [filename] + +Load modules from a BLIF file into the current design. +\end{lstlisting} + \section{read\_ilang -- read modules from ilang file} \label{cmd:read_ilang} \begin{lstlisting}[numbers=left,frame=single] @@ -1513,18 +1881,22 @@ Read cells from liberty file as modules into current design. set the specified attribute (to the value 1) on all loaded modules \end{lstlisting} -\section{read\_verilog -- read modules from verilog file} +\section{read\_verilog -- read modules from Verilog file} \label{cmd:read_verilog} \begin{lstlisting}[numbers=left,frame=single] read_verilog [options] [filename] -Load modules from a verilog file to the current design. A large subset of +Load modules from a Verilog file to the current design. A large subset of Verilog-2005 is supported. -sv enable support for SystemVerilog features. (only a small subset of SystemVerilog is supported) + -formal + enable support for assert() and assume() from SystemVerilog + replace the implicit -D SYNTHESIS with -D FORMAL + -dump_ast1 dump abstract syntax tree (before simplification) @@ -1532,7 +1904,7 @@ Verilog-2005 is supported. dump abstract syntax tree (after simplification) -dump_vlog - dump ast as verilog code (after simplification) + dump ast as Verilog code (after simplification) -yydebug enable parser debug output @@ -1554,19 +1926,31 @@ Verilog-2005 is supported. this can also be achieved by setting the 'nomem2reg' attribute on the respective module or register. + This is potentially dangerous. Usually the front-end has good + reasons for converting an array to a list of registers. + Prohibiting this step will likely result in incorrect synthesis + results. + -mem2reg always convert memories to registers. this can also be achieved by setting the 'mem2reg' attribute on the respective module or register. + -nomeminit + do not infer $meminit cells and instead convert initialized + memories to registers directly in the front-end. + -ppdump - dump verilog code after pre-processor + dump Verilog code after pre-processor -nopp do not run the pre-processor + -nodpi + disable DPI-C support + -lib - only create empty blackbox modules + only create empty blackbox modules. This implies -DBLACKBOX. -noopt don't perform basic optimizations (such as const folding) in the @@ -1584,6 +1968,9 @@ Verilog-2005 is supported. to a later 'hierarchy' command. Useful in cases where the default parameters of modules yield invalid or not synthesizable code. + -noautowire + make the default of `default_nettype be "none" instead of "wire". + -setattr <attribute_name> set the specified attribute (to the value 1) on all loaded modules @@ -1600,7 +1987,7 @@ subsequent calls to 'read_verilog'. Note that the Verilog frontend does a pretty good job of processing valid verilog input, but has not very good error reporting. It generally is -recommended to use a simulator (for example icarus verilog) for checking +recommended to use a simulator (for example Icarus Verilog) for checking the syntax of the code, rather than to rely on read_verilog for that. \end{lstlisting} @@ -1624,6 +2011,10 @@ pattern is '_%_'. Assign private names (the ones with $-prefix) to all selected wires and cells with public names. This ignores all selected ports. + + rename -top new_name + +Rename top module. \end{lstlisting} \section{sat -- solve a SAT problem in the circuit} @@ -1671,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 @@ -1683,11 +2077,17 @@ The following options can be used to set up a sequential problem: set up a sequential problem with <N> time steps. The steps will be numbered from 1 to N. + note: for large <N> it can be significantly faster to use + -tempinduct-baseonly -maxsteps <N> instead of -seq <N>. + -set-at <N> <signal> <value> -unset-at <N> <signal> set or unset the specified signal to the specified value in the given timestep. this has priority over a -set for the same signal. + -set-assumes + set all assumptions provided via $assume cells + -set-def-at <N> <signal> -set-any-undef-at <N> <signal> -set-all-undef-at <N> <signal> @@ -1708,6 +2108,9 @@ The following options can be used to set up a sequential problem: -dump_vcd <vcd-file-name> dump SAT model (counter example in proof) to VCD file + -dump_json <json-file-name> + dump SAT model (counter example in proof) to a WaveJSON file. + -dump_cnf <cnf-file-name> dump CNF of SAT problem (in DIMACS format). in temporal induction proofs this is the CNF of the first induction step. @@ -1716,7 +2119,7 @@ The following additional options can be used to set up a proof. If also -seq is passed, a temporal induction proof is performed. -tempinduct - Perform a temporal induction proof. In a temporalinduction proof it is + Perform a temporal induction proof. In a temporal induction proof it is proven that the condition holds forever after the number of time steps specified using -seq. @@ -1724,12 +2127,26 @@ is passed, a temporal induction proof is performed. Perform a temporal induction proof. Assume an initial state with all registers set to defined values for the induction step. + -tempinduct-baseonly + Run only the basecase half of temporal induction (requires -maxsteps) + + -tempinduct-inductonly + Run only the induction half of temporal induction + + -tempinduct-skip <N> + Skip the first <N> steps of the induction proof. + + note: this will assume that the base case holds for <N> steps. + this must be proven independently with "-tempinduct-baseonly + -maxsteps <N>". Use -initsteps if you just want to set a + minimal induction length. + -prove <signal> <value> Attempt to proof that <signal> is always <value>. -prove-x <signal> <value> Like -prove, but an undef (x) bit in the lhs matches any value on - the right hand side. Useful for equivialence checking. + the right hand side. Useful for equivalence checking. -prove-asserts Prove that all asserts in the design hold. @@ -1742,6 +2159,13 @@ is passed, a temporal induction proof is performed. -initsteps <N> Set initial length for the induction. + This will speed up the search of the right induction length + for deep induction proofs. + + -stepsize <N> + Increase the size of the induction proof in steps of <N>. + This will speed up the search of the right induction length + for deep induction proofs. -timeout <N> Maximum number of seconds a single SAT instance may take. @@ -1780,10 +2204,18 @@ Use the opt_clean command to get rid of the additional nets. This command identifies strongly connected components (aka logic loops) in the design. + -expect <num> + expect to find exactly <num> SSCs. A different number of SSCs will + produce an error. + -max_depth <num> - limit to loops not longer than the specified number of cells. This can - e.g. be useful in identifying local loops in a module that turns out - to be one gigantic SCC. + limit to loops not longer than the specified number of cells. This + can e.g. be useful in identifying small local loops in a module that + implements one large SCC. + + -nofeedback + do not count cells that have their output fed back into one of their + inputs as single-cell scc. -all_cell_types Usually this command only considers internal non-memory cells. With @@ -1978,7 +2410,7 @@ The following actions can be performed on the top sets on the stack: like %d but swap the roles of two top sets on the stack %c - create a copy of the top set rom the stack and push it + create a copy of the top set from the stack and push it %x[<num1>|*][.<num2>][:<rule>[:<rule>..]] expand top set <num1> num times according to the specified rules. @@ -1995,19 +2427,31 @@ The following actions can be performed on the top sets on the stack: %ci[<num1>|*][.<num2>][:<rule>[:<rule>..]] %co[<num1>|*][.<num2>][:<rule>[:<rule>..]] - simmilar to %x, but only select input (%ci) or output cones (%co) + similar to %x, but only select input (%ci) or output cones (%co) + + %xe[...] %cie[...] %coe + like %x, %ci, and %co but only consider combinatorial cells %a expand top set by selecting all wires that are (at least in part) aliases for selected wires. %s - expand top set by adding all modules of instantiated cells in selected + expand top set by adding all modules that implement cells in selected modules %m expand top set by selecting all modules that contain selected objects + %M + select modules that implement selected cells + + %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: @@ -2080,7 +2524,7 @@ is used to determine if two resources are share-able. -fast Only consider the simple part of the control logic in SAT solving, resulting - in much easier SAT problems at the cost of maybe missing some oportunities + in much easier SAT problems at the cost of maybe missing some opportunities for resource sharing. -limit N @@ -2140,6 +2584,10 @@ to a graphics file (usually SVG or PostScript). inputs or outputs. This option can be used multiple times to specify more than one library. + note: in most cases it is better to load the library before calling + show with 'read_verilog -lib <filename>'. it is also possible to + load liberty files with 'read_liberty -lib <filename>'. + -prefix <prefix> generate <prefix>.* instead of ~/.yosys_show.* @@ -2156,7 +2604,7 @@ to a graphics file (usually SVG or PostScript). -colors <seed> Randomly assign colors to the wires. The integer argument is the seed for the random number generator. Change the seed value if the colored - graph still is ambigous. A seed of zero deactivates the coloring. + graph still is ambiguous. A seed of zero deactivates the coloring. -colorattr <attribute_name> Use the specified attribute to assign colors. A unique color is @@ -2166,7 +2614,7 @@ to a graphics file (usually SVG or PostScript). annotate busses with a label indicating the width of the bus. -signed - mark ports (A, B) that are declarted as signed (using the [AB]_SIGNED + mark ports (A, B) that are declared as signed (using the [AB]_SIGNED cell parameter) with an asterisk next to the port name. -stretch @@ -2180,7 +2628,7 @@ to a graphics file (usually SVG or PostScript). enumerate objects with internal ($-prefixed) names -long - do not abbeviate objects with internal ($-prefixed) names + do not abbreviate objects with internal ($-prefixed) names -notitle do not add the module name as graph title to the dot file @@ -2190,6 +2638,9 @@ specified, 'xdot' is used to display the schematic. The generated output files are '~/.yosys_show.dot' and '~/.yosys_show.<format>', unless another prefix is specified using -prefix <prefix>. + +Yosys on Windows and YosysJS use different defaults: The output is written +to 'show.dot' in the current directory and new viewer is launched. \end{lstlisting} \section{simplemap -- mapping simple coarse-grain cells} @@ -2202,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] @@ -2213,7 +2680,7 @@ primitives. The following internal cell types are mapped by this pass: This command adds $slice and $concat cells to the design to make the splicing of multi-bit signals explicit. This for example is useful for coarse grain -synthesis, where dedidacted hardware is needed to splice signals. +synthesis, where dedicated hardware is needed to splice signals. -sel_by_cell only select the cell ports to rewire by the cell. if the selection @@ -2228,6 +2695,9 @@ synthesis, where dedidacted hardware is needed to splice signals. it is sufficient if the driver of any bit of a cell port is selected. by default all bits must be selected. + -wires + also add $slice and $concat cells to drive otherwise unused wires. + -no_outputs do not rewire selected module outputs. @@ -2277,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. @@ -2285,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 @@ -2298,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} @@ -2319,9 +2796,19 @@ on partly selected designs. -encfile <file> passed to 'fsm_recode' via 'fsm' + -nofsm + do not run FSM optimization + -noabc do not run abc (as if yosys was compiled without ABC support) + -noalumacc + do not run 'alumacc' pass. i.e. keep arithmetic operators in + their direct form ($add, $sub, etc.). + + -nordff + passed to 'memory'. 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 @@ -2335,6 +2822,9 @@ The following commands are executed by this synthesis command: coarse: proc + opt_const + opt_clean + check opt wreduce alumacc @@ -2351,10 +2841,183 @@ The following commands are executed by this synthesis command: opt -full techmap opt -fast - - abc: abc -fast opt -fast + + check: + hierarchy -check + stat + 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] + synth_ice40 [options] + +This command runs synthesis for iCE40 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 + + -nocarry + do not use SB_CARRY cells in output netlist + + -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: + + begin: + read_verilog -lib +/ice40/cells_sim.v + hierarchy -check -top <top> + + flatten: (unless -noflatten) + proc + flatten + tribuf -logic + + coarse: + synth -run coarse + + bram: (skip if -nobram) + memory_bram -rules +/ice40/brams.txt + techmap -map +/ice40/brams_map.v + + fine: + opt -fast -mux_undef -undriven -fine + memory_map + opt -undriven -fine + techmap -map +/techmap.v [-map +/ice40/arith_map.v] + abc -dff (only if -retime) + 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 + + map_cells: + techmap -map +/ice40/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\_xilinx -- synthesis for Xilinx FPGAs} @@ -2367,7 +3030,7 @@ partly selected designs. At the moment this command creates netlists that are compatible with 7-Series Xilinx devices. -top <module> - use the specified module as top module (default='top') + use the specified module as top module -edif <file> write the design to the specified edif file. writing of an output file @@ -2389,6 +3052,8 @@ The following commands are executed by this synthesis command: begin: read_verilog -lib +/xilinx/cells_sim.v + read_verilog -lib +/xilinx/brams_bb.v + read_verilog -lib +/xilinx/drams_bb.v hierarchy -check -top <top> flatten: (only if -flatten) @@ -2397,29 +3062,40 @@ The following commands are executed by this synthesis command: coarse: synth -run coarse - dff2dffe bram: memory_bram -rules +/xilinx/brams.txt techmap -map +/xilinx/brams_map.v + dram: + memory_bram -rules +/xilinx/drams.txt + techmap -map +/xilinx/drams_map.v + 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: techmap -map +/xilinx/cells_map.v + dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT clean - edif: - write_edif synth.edif + check: + hierarchy -check + stat + check -noinit + + edif: (only if -edif) + write_edif <file-name> \end{lstlisting} \section{tcl -- execute a TCL script file} @@ -2433,7 +3109,7 @@ Use 'yosys cmd' to run the yosys command 'cmd' from tcl. The tcl command 'yosys -import' can be used to import all yosys commands directly as tcl commands to the tcl shell. The yosys command 'proc' is wrapped using the tcl command 'procs' in order -to avoid a name collision with the tcl builting command 'proc'. +to avoid a name collision with the tcl builtin command 'proc'. \end{lstlisting} \section{techmap -- generic technology mapper} @@ -2442,7 +3118,7 @@ to avoid a name collision with the tcl builting command 'proc'. techmap [-map filename] [selection] This pass implements a very simple technology mapper that replaces cells in -the design with implementations given in form of a verilog or ilang source +the design with implementations given in form of a Verilog or ilang source file. -map filename @@ -2454,11 +3130,6 @@ file. -map %<design-name> like -map above, but with an in-memory design instead of a file. - -share_map filename - like -map, but look for the file in the share directory (where the - yosys data files are). this is mainly used internally when techmap - is called from other commands. - -extern load the cell implementations as separate modules into the design instead of inlining them. @@ -2468,7 +3139,7 @@ file. -recursive instead of the iterative breadth-first algorithm use a recursive - depth-first algorithm. both methods should yield equivialent results, + depth-first algorithm. both methods should yield equivalent results, but may differ in performance. -autoproc @@ -2480,8 +3151,8 @@ file. as final cell types by this mode. -D <define>, -I <incdir> - this options are passed as-is to the verilog frontend for loading the - map file. Note that the verilog frontend is also called with the + this options are passed as-is to the Verilog frontend for loading the + map file. Note that the Verilog frontend is also called with the '-ignore_redef' option set. When a module in the map file has the 'techmap_celltype' attribute set, it will @@ -2527,7 +3198,7 @@ wires are supported: of constant inputs and shorted inputs at this point and import the constant and connected bits into the map module. All further commands are executed in this copy. This is a very convenient way of creating - optimizied specializations of techmap modules without using the special + optimized specializations of techmap modules without using the special parameters described below. A _TECHMAP_DO_* command may start with the special token 'RECURSION; '. @@ -2563,12 +3234,12 @@ the design is connected to a constant value. The parameter is then set to the constant value. A cell with the name _TECHMAP_REPLACE_ in the map file will inherit the name -of the cell that is beeing replaced. +of the cell that is being replaced. See 'help extract' for a pass that does the opposite thing. See 'help flatten' for a pass that does flatten the design (which is -esentially techmap but using the design itself as map library). +essentially techmap but using the design itself as map library). \end{lstlisting} \section{tee -- redirect command output to file} @@ -2608,7 +3279,7 @@ Test handling of logic loops in ABC. \begin{lstlisting}[numbers=left,frame=single] test_autotb [options] [filename] -Automatically create primitive verilog test benches for all modules in the +Automatically create primitive Verilog test benches for all modules in the design. The generated testbenches toggle the input pins of the module in a semi-random manner and dumps the resulting output signals. @@ -2624,7 +3295,7 @@ value after initialization. This can e.g. be used to force a reset signal low in order to explore more inner states in a state machine. -n <int> - number of iterations the test bench shuld run (default = 1000) + number of iterations the test bench should run (default = 1000) \end{lstlisting} \section{test\_cell -- automatically test the implementation of a cell type} @@ -2657,6 +3328,9 @@ cell types. Use for example 'all /$add' for all cell types except $add. -simlib use "techmap -map +/simlib.v -max_iter 2 -autoproc" + -aigmap + instead of calling "techmap", call "aigmap" + -muxdiv when creating test benches with dividers, create an additional mux to mask out the division-by-zero case @@ -2670,11 +3344,29 @@ 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 -vlog {filename} - create a verilog test bench to test simlib and write_verilog + 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} @@ -2686,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] @@ -2701,7 +3409,7 @@ Load the specified VHDL files into Verific. verific -import [-gates] {-all | <top-module>..} -Elaborate the design for the sepcified top modules, import to Yosys and +Elaborate the design for the specified top modules, import to Yosys and reset the internal state of Verific. A gate-level netlist is created when called with -gates. @@ -2713,11 +3421,11 @@ Visit http://verific.com/ for more information on Verific. \begin{lstlisting}[numbers=left,frame=single] verilog_defaults -add [options] -Add the sepcified options to the list of default options to read_verilog. +Add the specified options to the list of default options to read_verilog. verilog_defaults -clear -Clear the list of verilog default options. +Clear the list of Verilog default options. verilog_defaults -push verilog_defaults -pop @@ -2761,7 +3469,7 @@ vhdl2verilog can be obtained from: http://www.edautils.com/vhdl2verilog.html \end{lstlisting} -\section{wreduce -- reduce the word size of operations is possible} +\section{wreduce -- reduce the word size of operations if possible} \label{cmd:wreduce} \begin{lstlisting}[numbers=left,frame=single] wreduce [options] [selection] @@ -2815,8 +3523,14 @@ file *.blif when any of this options is used. do not generate buffers for connected wires. instead use the non-standard .conn statement. + -attr + use the non-standard .attr statement to write cell attributes + -param - use the non-standard .param statement to write module parameters + 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. @@ -2854,7 +3568,7 @@ is targeted. \begin{lstlisting}[numbers=left,frame=single] write_file [options] output_file [input_file] -Write the text fron the input file to the output file. +Write the text from the input file to the output file. -a Append to output file (instead of overwriting) @@ -2903,14 +3617,217 @@ a tool for Coarse-Grain Example-Driven Interconnect Synthesis. http://www.clifford.at/intersynth/ \end{lstlisting} +\section{write\_json -- write design to a JSON file} +\label{cmd:write_json} +\begin{lstlisting}[numbers=left,frame=single] + write_json [options] [filename] + +Write a JSON netlist of the current design. + + -aig + include AIG models for the different gate types + + +The general syntax of the JSON output created by this command is as follows: + + { + "modules": { + <module_name>: { + "ports": { + <port_name>: <port_details>, + ... + }, + "cells": { + <cell_name>: <cell_details>, + ... + }, + "netnames": { + <net_name>: <net_details>, + ... + } + } + }, + "models": { + ... + }, + } + +Where <port_details> is: + + { + "direction": <"input" | "output" | "inout">, + "bits": <bit_vector> + } + +And <cell_details> is: + + { + "hide_name": <1 | 0>, + "type": <cell_type>, + "parameters": { + <parameter_name>: <parameter_value>, + ... + }, + "attributes": { + <attribute_name>: <attribute_value>, + ... + }, + "port_directions": { + <port_name>: <"input" | "output" | "inout">, + ... + }, + "connections": { + <port_name>: <bit_vector>, + ... + }, + } + +And <net_details> is: + + { + "hide_name": <1 | 0>, + "bits": <bit_vector> + } + +The "hide_name" fields are set to 1 when the name of this cell or net is +automatically created and is likely not of interest for a regular user. + +The "port_directions" section is only included for cells for which the +interface is known. + +Module and cell ports and nets can be single bit wide or vectors of multiple +bits. Each individual signal bit is assigned a unique integer. The <bit_vector> +values referenced above are vectors of this integers. Signal bits that are +connected to a constant driver are denoted as string "0" or "1" instead of +a number. + +For example the following Verilog code: + + module test(input x, y); + (* keep *) foo #(.P(42), .Q(1337)) + foo_inst (.A({x, y}), .B({y, x}), .C({4'd10, {4{x}}})); + endmodule + +Translates to the following JSON output: + + { + "modules": { + "test": { + "ports": { + "x": { + "direction": "input", + "bits": [ 2 ] + }, + "y": { + "direction": "input", + "bits": [ 3 ] + } + }, + "cells": { + "foo_inst": { + "hide_name": 0, + "type": "foo", + "parameters": { + "Q": 1337, + "P": 42 + }, + "attributes": { + "keep": 1, + "src": "test.v:2" + }, + "connections": { + "C": [ 2, 2, 2, 2, "0", "1", "0", "1" ], + "B": [ 2, 3 ], + "A": [ 3, 2 ] + } + } + }, + "netnames": { + "y": { + "hide_name": 0, + "bits": [ 3 ], + "attributes": { + "src": "test.v:1" + } + }, + "x": { + "hide_name": 0, + "bits": [ 2 ], + "attributes": { + "src": "test.v:1" + } + } + } + } + } + } + +The models are given as And-Inverter-Graphs (AIGs) in the following form: + + "models": { + <model_name>: [ + /* 0 */ [ <node-spec> ], + /* 1 */ [ <node-spec> ], + /* 2 */ [ <node-spec> ], + ... + ], + ... + }, + +The following node-types may be used: + + [ "port", <portname>, <bitindex>, <out-list> ] + - the value of the specified input port bit + + [ "nport", <portname>, <bitindex>, <out-list> ] + - the inverted value of the specified input port bit + + [ "and", <node-index>, <node-index>, <out-list> ] + - the ANDed value of the specified nodes + + [ "nand", <node-index>, <node-index>, <out-list> ] + - the inverted ANDed value of the specified nodes + + [ "true", <out-list> ] + - the constant value 1 + + [ "false", <out-list> ] + - the constant value 0 + +All nodes appear in topological order. I.e. only nodes with smaller indices +are referenced by "and" and "nand" nodes. + +The optional <out-list> at the end of a node specification is a list of +output portname and bitindex pairs, specifying the outputs driven by this node. + +For example, the following is the model for a 3-input 3-output $reduce_and cell +inferred by the following code: + + module test(input [2:0] in, output [2:0] out); + assign in = &out; + endmodule + + "$reduce_and:3U:3": [ + /* 0 */ [ "port", "A", 0 ], + /* 1 */ [ "port", "A", 1 ], + /* 2 */ [ "and", 0, 1 ], + /* 3 */ [ "port", "A", 2 ], + /* 4 */ [ "and", 2, 3, "Y", 0 ], + /* 5 */ [ "false", "Y", 1, "Y", 2 ] + ] + +Future version of Yosys might add support for additional fields in the JSON +format. A program processing this format must ignore all unknown fields. +\end{lstlisting} + \section{write\_smt2 -- write design to SMT-LIBv2 file} \label{cmd:write_smt2} \begin{lstlisting}[numbers=left,frame=single] write_smt2 [options] [filename] Write a SMT-LIBv2 [1] description of the current design. For a module with name -'<mod>' this will declare the sort '<mod>_s' (state of the module) and the the -function '<mod>_t' (state transition function). +'<mod>' this will declare the sort '<mod>_s' (state of the module) and the +functions operating on that state. The '<mod>_s' sort represents a module state. Additional '<mod>_n' functions are provided that can be used to access the values of the signals in the module. @@ -2922,11 +3839,37 @@ multi-bit wires are exported as single functions of type BitVec. The '<mod>_t' function evaluates to 'true' when the given pair of states describes a valid state transition. +The '<mod>_a' function evaluates to 'true' when the given state satisfies +the asserts in the module. + +The '<mod>_u' function evaluates to 'true' when the given state satisfies +the assumptions in the module. + +The '<mod>_i' function evaluates to 'true' when the given state conforms +to the initial state. + + -verbose + this will print the recursive walk used to export the modules. + -bv enable support for BitVec (FixedSizeBitVectors theory). with this option set multi-bit wires are represented using the BitVec sort and support for coarse grain cells (incl. arithmetic) is enabled. + -mem + enable support for memories (via ArraysEx theory). this option + also implies -bv. only $mem cells without merged registers in + read ports are supported. call "memory" with -nordff to make sure + that no registers are merged into $mem read ports. '<mod>_m' functions + will be generated for accessing the arrays that are used to represent + memories. + + -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. @@ -2973,13 +3916,30 @@ For this proof we create the following template (test.tpl). The following yosys script will create a 'test.smt2' file for our proof: read_verilog test.v - hierarchy; proc; techmap; opt -fast + hierarchy -check; proc; opt; check -assert write_smt2 -bv -tpl test.tpl test.smt2 Running 'cvc4 test.smt2' will print 'unsat' because y can never transition from non-zero to zero in the test design. \end{lstlisting} +\section{write\_smv -- write design to SMV file} +\label{cmd:write_smv} +\begin{lstlisting}[numbers=left,frame=single] + write_smv [options] [filename] + +Write an SMV description of the current design. + + -verbose + this will print the recursive walk used to export the modules. + + -tpl <template_file> + use the given template file. the line containing only the token '%%' + is replaced with the regular output of this command. + +THIS COMMAND IS UNDER CONSTRUCTION +\end{lstlisting} + \section{write\_spice -- write design to SPICE netlist file} \label{cmd:write_spice} \begin{lstlisting}[numbers=left,frame=single] @@ -2988,7 +3948,7 @@ from non-zero to zero in the test design. Write the current design to an SPICE netlist file. -big_endian - generate multi-bit ports in MSB first order + generate multi-bit ports in MSB first order (default is LSB first) -neg net_name @@ -3004,12 +3964,12 @@ Write the current design to an SPICE netlist file. set the specified module as design top module \end{lstlisting} -\section{write\_verilog -- write design to verilog file} +\section{write\_verilog -- write design to Verilog file} \label{cmd:write_verilog} \begin{lstlisting}[numbers=left,frame=single] write_verilog [options] [filename] -Write the current design to a verilog file. +Write the current design to a Verilog file. -norename without this option all internal object names (the ones with a dollar @@ -3023,7 +3983,7 @@ Write the current design to a verilog file. with this option attributes are included as comments in the output -noexpr - without this option all internal cells are converted to verilog + without this option all internal cells are converted to Verilog expressions. -blackboxes diff --git a/manual/literature.bib b/manual/literature.bib index 91bc1f382..372e882ac 100644 --- a/manual/literature.bib +++ b/manual/literature.bib @@ -1,163 +1,163 @@ -
-@inproceedings{intersynth,
- title={Example-driven interconnect synthesis for heterogeneous coarse-grain reconfigurable logic},
- author={Clifford Wolf and Johann Glaser and Florian Schupfer and Jan Haase and Christoph Grimm},
- booktitle={FDL Proceeding of the 2012 Forum on Specification and Design Languages},
- pages={194--201},
- year={2012}
-}
-
-@incollection{intersynthFdlBookChapter,
- title={Methodology and Example-Driven Interconnect Synthesis for Designing Heterogeneous Coarse-Grain Reconfigurable Architectures},
- author={Johann Glaser and Clifford Wolf},
- booktitle={Advances in Models, Methods, and Tools for Complex Chip Design --- Selected contributions from FDL'12},
- editor={Jan Haase},
- publisher={Springer},
- year={2013},
- note={to appear}
-}
-
-@unpublished{BACC,
- author = {Clifford Wolf},
- title = {Design and Implementation of the Yosys Open SYnthesis Suite},
- note = {Bachelor Thesis, Vienna University of Technology},
- year = {2013}
-}
-
-@unpublished{VerilogFossEval,
- author = {Clifford Wolf},
- title = {Evaluation of Open Source Verilog Synthesis Tools for Feature-Completeness and Extensibility},
- note = {Unpublished Student Research Paper, Vienna University of Technology},
- year = {2012}
-}
-
-@article{ABEL,
- title={A High-Level Design Language for Programmable Logic Devices},
- author={Kyu Y. Lee and Michael Holley and Mary Bailey and Walter Bright},
- journal={VLSI Design (Manhasset NY: CPM Publications)},
- year={June 1985},
- pages={50-62}
-}
-
-@MISC{Cheng93vl2mv:a,
- author = {S-T Cheng and G York and R K Brayton},
- title = {VL2MV: A Compiler from Verilog to BLIF-MV},
- year = {1993}
-}
-
-@MISC{Odin,
- author = {Peter Jamieson and Jonathan Rose},
- title = {A VERILOG RTL SYNTHESIS TOOL FOR HETEROGENEOUS FPGAS},
- year = {2005}
-}
-
-@inproceedings{vtr2012,
- title={The VTR Project: Architecture and CAD for FPGAs from Verilog to Routing},
- author={Jonathan Rose and Jason Luu and Chi Wai Yu and Opal Densmore and Jeff Goeders and Andrew Somerville and Kenneth B. Kent and Peter Jamieson and Jason Anderson},
- booktitle={Proceedings of the 20th ACM/SIGDA International Symposium on Field-Programmable Gate Arrays},
- pages={77--86},
- year={2012},
- organization={ACM}
-}
-
-@MISC{LogicSynthesis,
- author = {G D Hachtel and F Somenzi},
- title = {Logic Synthesis and Verification Algorithms},
- year = {1996}
-}
-
-@ARTICLE{Verilog2005,
- journal={IEEE Std 1364-2005 (Revision of IEEE Std 1364-2001)},
- title={IEEE Standard for Verilog Hardware Description Language},
- year={2006},
- doi={10.1109/IEEESTD.2006.99495}
-}
-
-@ARTICLE{VerilogSynth,
- journal={IEEE Std 1364.1-2002},
- title={IEEE Standard for Verilog Register Transfer Level Synthesis},
- year={2002},
- doi={10.1109/IEEESTD.2002.94220}
-}
-
-@ARTICLE{VHDL,
- journal={IEEE Std 1076-2008 (Revision of IEEE Std 1076-2002)}, title={IEEE Standard VHDL Language Reference Manual},
- year={2009},
- month={26},
- doi={10.1109/IEEESTD.2009.4772740}
-}
-
-@ARTICLE{VHDLSynth,
- journal={IEEE Std 1076.6-2004 (Revision of IEEE Std 1076.6-1999)}, title={IEEE Standard for VHDL Register Transfer Level (RTL) Synthesis},
- year={2004},
- doi={10.1109/IEEESTD.2004.94802}
-}
-
-@ARTICLE{IP-XACT,
-journal={IEEE Std 1685-2009}, title={IEEE Standard for IP-XACT, Standard Structure for Packaging, Integrating, and Reusing IP within Tools Flows},
-year={2010},
-pages={C1-360},
-keywords={abstraction definitions, address space specification, bus definitions, design environment, EDA, electronic design automation, electronic system level, ESL, implementation constraints, IP-XACT, register transfer level, RTL, SCRs, semantic consistency rules, TGI, tight generator interface, tool and data interoperability, use models, XML design meta-data, XML schema},
-doi={10.1109/IEEESTD.2010.5417309},}
-
-@book{Dragonbook,
-author = {Aho, Alfred V. and Sethi, Ravi and Ullman, Jeffrey D.},
-title = {Compilers: principles, techniques, and tools},
-year = {1986},
-isbn = {0-201-10088-6},
-publisher = {Addison-Wesley Longman Publishing Co., Inc.},
-address = {Boston, MA, USA},
-}
-
-@INPROCEEDINGS{Cummings00,
-author = {Clifford E. Cummings and Sunburst Design Inc},
-title = {Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill},
-booktitle = {SNUG (Synopsys Users Group) 2000 User Papers, section-MC1 (1 st paper},
-year = {2000}
-}
-
-@ARTICLE{MURPHY,
- author={D. L. Klipstein},
- journal={Cahners Publishing Co., EEE Magazine, Vol. 15, No. 8},
- title={The Contributions of Edsel Murphy to the Understanding of the Behavior of Inanimate Objects},
- year={August 1967}
-}
-
-@INPROCEEDINGS{fsmextract,
-author={Yiqiong Shi and Chan Wai Ting and Bah-Hwee Gwee and Ye Ren},
-booktitle={Circuits and Systems (ISCAS), Proceedings of 2010 IEEE International Symposium on},
-title={A highly efficient method for extracting FSMs from flattened gate-level netlist},
-year={2010},
-pages={2610-2613},
-keywords={circuit CAD;finite state machines;microcontrollers;FSM;control-intensive circuits;finite state machines;flattened gate-level netlist;state register elimination technique;Automata;Circuit synthesis;Continuous wavelet transforms;Design automation;Digital circuits;Hardware design languages;Logic;Microcontrollers;Registers;Signal processing},
-doi={10.1109/ISCAS.2010.5537093},}
-
-@ARTICLE{MultiLevelLogicSynth,
-author={Brayton, R.K. and Hachtel, G.D. and Sangiovanni-Vincentelli, A.L.},
-journal={Proceedings of the IEEE},
-title={Multilevel logic synthesis},
-year={1990},
-volume={78},
-number={2},
-pages={264-300},
-keywords={circuit layout CAD;integrated logic circuits;logic CAD;capsule summaries;definitions;detailed analysis;in-depth background;logic decomposition;logic minimisation;logic synthesis;logic synthesis techniques;multilevel combinational logic;multilevel logic synthesis;notation;perspective;survey;synthesis methods;technology mapping;testing;Application specific integrated circuits;Design automation;Integrated circuit synthesis;Logic design;Logic devices;Logic testing;Network synthesis;Programmable logic arrays;Signal synthesis;Silicon},
-doi={10.1109/5.52213},
-ISSN={0018-9219},}
-
-@article{UllmannSubgraphIsomorphism,
- author = {Ullmann, J. R.},
- title = {An Algorithm for Subgraph Isomorphism},
- journal = {J. ACM},
- issue_date = {Jan. 1976},
- volume = {23},
- number = {1},
- month = jan,
- year = {1976},
- issn = {0004-5411},
- pages = {31--42},
- numpages = {12},
- doi = {10.1145/321921.321925},
- acmid = {321925},
- publisher = {ACM},
- address = {New York, NY, USA},
-}
+ +@inproceedings{intersynth, + title={Example-driven interconnect synthesis for heterogeneous coarse-grain reconfigurable logic}, + author={Clifford Wolf and Johann Glaser and Florian Schupfer and Jan Haase and Christoph Grimm}, + booktitle={FDL Proceeding of the 2012 Forum on Specification and Design Languages}, + pages={194--201}, + year={2012} +} + +@incollection{intersynthFdlBookChapter, + title={Methodology and Example-Driven Interconnect Synthesis for Designing Heterogeneous Coarse-Grain Reconfigurable Architectures}, + author={Johann Glaser and Clifford Wolf}, + booktitle={Advances in Models, Methods, and Tools for Complex Chip Design --- Selected contributions from FDL'12}, + editor={Jan Haase}, + publisher={Springer}, + year={2013}, + note={to appear} +} + +@unpublished{BACC, + author = {Clifford Wolf}, + title = {Design and Implementation of the Yosys Open SYnthesis Suite}, + note = {Bachelor Thesis, Vienna University of Technology}, + year = {2013} +} + +@unpublished{VerilogFossEval, + author = {Clifford Wolf}, + title = {Evaluation of Open Source Verilog Synthesis Tools for Feature-Completeness and Extensibility}, + note = {Unpublished Student Research Paper, Vienna University of Technology}, + year = {2012} +} + +@article{ABEL, + title={A High-Level Design Language for Programmable Logic Devices}, + author={Kyu Y. Lee and Michael Holley and Mary Bailey and Walter Bright}, + journal={VLSI Design (Manhasset NY: CPM Publications)}, + year={June 1985}, + pages={50-62} +} + +@MISC{Cheng93vl2mv:a, + author = {S-T Cheng and G York and R K Brayton}, + title = {VL2MV: A Compiler from Verilog to BLIF-MV}, + year = {1993} +} + +@MISC{Odin, + author = {Peter Jamieson and Jonathan Rose}, + title = {A VERILOG RTL SYNTHESIS TOOL FOR HETEROGENEOUS FPGAS}, + year = {2005} +} + +@inproceedings{vtr2012, + title={The VTR Project: Architecture and CAD for FPGAs from Verilog to Routing}, + author={Jonathan Rose and Jason Luu and Chi Wai Yu and Opal Densmore and Jeff Goeders and Andrew Somerville and Kenneth B. Kent and Peter Jamieson and Jason Anderson}, + booktitle={Proceedings of the 20th ACM/SIGDA International Symposium on Field-Programmable Gate Arrays}, + pages={77--86}, + year={2012}, + organization={ACM} +} + +@MISC{LogicSynthesis, + author = {G D Hachtel and F Somenzi}, + title = {Logic Synthesis and Verification Algorithms}, + year = {1996} +} + +@ARTICLE{Verilog2005, + journal={IEEE Std 1364-2005 (Revision of IEEE Std 1364-2001)}, + title={IEEE Standard for Verilog Hardware Description Language}, + year={2006}, + doi={10.1109/IEEESTD.2006.99495} +} + +@ARTICLE{VerilogSynth, + journal={IEEE Std 1364.1-2002}, + title={IEEE Standard for Verilog Register Transfer Level Synthesis}, + year={2002}, + doi={10.1109/IEEESTD.2002.94220} +} + +@ARTICLE{VHDL, + journal={IEEE Std 1076-2008 (Revision of IEEE Std 1076-2002)}, title={IEEE Standard VHDL Language Reference Manual}, + year={2009}, + month={26}, + doi={10.1109/IEEESTD.2009.4772740} +} + +@ARTICLE{VHDLSynth, + journal={IEEE Std 1076.6-2004 (Revision of IEEE Std 1076.6-1999)}, title={IEEE Standard for VHDL Register Transfer Level (RTL) Synthesis}, + year={2004}, + doi={10.1109/IEEESTD.2004.94802} +} + +@ARTICLE{IP-XACT, +journal={IEEE Std 1685-2009}, title={IEEE Standard for IP-XACT, Standard Structure for Packaging, Integrating, and Reusing IP within Tools Flows}, +year={2010}, +pages={C1-360}, +keywords={abstraction definitions, address space specification, bus definitions, design environment, EDA, electronic design automation, electronic system level, ESL, implementation constraints, IP-XACT, register transfer level, RTL, SCRs, semantic consistency rules, TGI, tight generator interface, tool and data interoperability, use models, XML design meta-data, XML schema}, +doi={10.1109/IEEESTD.2010.5417309},} + +@book{Dragonbook, +author = {Aho, Alfred V. and Sethi, Ravi and Ullman, Jeffrey D.}, +title = {Compilers: principles, techniques, and tools}, +year = {1986}, +isbn = {0-201-10088-6}, +publisher = {Addison-Wesley Longman Publishing Co., Inc.}, +address = {Boston, MA, USA}, +} + +@INPROCEEDINGS{Cummings00, +author = {Clifford E. Cummings and Sunburst Design Inc}, +title = {Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill}, +booktitle = {SNUG (Synopsys Users Group) 2000 User Papers, section-MC1 (1 st paper}, +year = {2000} +} + +@ARTICLE{MURPHY, + author={D. L. Klipstein}, + journal={Cahners Publishing Co., EEE Magazine, Vol. 15, No. 8}, + title={The Contributions of Edsel Murphy to the Understanding of the Behavior of Inanimate Objects}, + year={August 1967} +} + +@INPROCEEDINGS{fsmextract, +author={Yiqiong Shi and Chan Wai Ting and Bah-Hwee Gwee and Ye Ren}, +booktitle={Circuits and Systems (ISCAS), Proceedings of 2010 IEEE International Symposium on}, +title={A highly efficient method for extracting FSMs from flattened gate-level netlist}, +year={2010}, +pages={2610-2613}, +keywords={circuit CAD;finite state machines;microcontrollers;FSM;control-intensive circuits;finite state machines;flattened gate-level netlist;state register elimination technique;Automata;Circuit synthesis;Continuous wavelet transforms;Design automation;Digital circuits;Hardware design languages;Logic;Microcontrollers;Registers;Signal processing}, +doi={10.1109/ISCAS.2010.5537093},} + +@ARTICLE{MultiLevelLogicSynth, +author={Brayton, R.K. and Hachtel, G.D. and Sangiovanni-Vincentelli, A.L.}, +journal={Proceedings of the IEEE}, +title={Multilevel logic synthesis}, +year={1990}, +volume={78}, +number={2}, +pages={264-300}, +keywords={circuit layout CAD;integrated logic circuits;logic CAD;capsule summaries;definitions;detailed analysis;in-depth background;logic decomposition;logic minimisation;logic synthesis;logic synthesis techniques;multilevel combinational logic;multilevel logic synthesis;notation;perspective;survey;synthesis methods;technology mapping;testing;Application specific integrated circuits;Design automation;Integrated circuit synthesis;Logic design;Logic devices;Logic testing;Network synthesis;Programmable logic arrays;Signal synthesis;Silicon}, +doi={10.1109/5.52213}, +ISSN={0018-9219},} + +@article{UllmannSubgraphIsomorphism, + author = {Ullmann, J. R.}, + title = {An Algorithm for Subgraph Isomorphism}, + journal = {J. ACM}, + issue_date = {Jan. 1976}, + volume = {23}, + number = {1}, + month = jan, + year = {1976}, + issn = {0004-5411}, + pages = {31--42}, + numpages = {12}, + doi = {10.1145/321921.321925}, + acmid = {321925}, + publisher = {ACM}, + address = {New York, NY, USA}, +} diff --git a/manual/manual.tex b/manual/manual.tex index 84be86e5b..67982cbc8 100644 --- a/manual/manual.tex +++ b/manual/manual.tex @@ -17,7 +17,7 @@ % (i.e. run "sudo su" and then execute the commands in the root % shell, don't just prefix the commands with "sudo"). -% formats the text accourding the set language +% formats the text according the set language \usepackage[english]{babel} \usepackage[table,usenames]{xcolor} % generates indices with the "\index" command @@ -151,14 +151,14 @@ availability of a Free and Open Source (FOSS) synthesis tool that can be used as basis for custom tools would be helpful. In the absence of such a tool, the Yosys Open SYnthesis Suite (Yosys) was -developped. This document covers the design and implementation of this tool. +developed. This document covers the design and implementation of this tool. At the moment the main focus of Yosys lies on the high-level aspects of digital synthesis. The pre-existing FOSS logic-synthesis tool ABC is used by Yosys to perform advanced gate-level optimizations. An evaluation of Yosys based on real-world designs is included. It is shown that Yosys can be used as-is to synthesize such designs. The results produced -by Yosys in this tests where successflly verified using formal verification +by Yosys in this tests where successfully verified using formal verification and are comparable in quality to the results produced by a commercial synthesis tool. @@ -172,7 +172,7 @@ University of Technology \cite{BACC}. AIG & And-Inverter-Graph \\ ASIC & Application-Specific Integrated Circuit \\ AST & Abstract Syntax Tree \\ -BDD & Binary Decicion Diagram \\ +BDD & Binary Decision Diagram \\ BLIF & Berkeley Logic Interchange Format \\ EDA & Electronic Design Automation \\ EDIF & Electronic Design Interchange Format \\ diff --git a/manual/presentation.tex b/manual/presentation.tex index dc5127755..63b963bbd 100644 --- a/manual/presentation.tex +++ b/manual/presentation.tex @@ -18,7 +18,7 @@ % (i.e. run "sudo su" and then execute the commands in the root % shell, don't just prefix the commands with "sudo"). -% formats the text accourding the set language +% formats the text according the set language \usepackage[english]{babel} \usepackage{amsmath} \usepackage{multirow} @@ -118,7 +118,7 @@ hobbyists, educators and engineers alike. \bigskip This presentation covers basic concepts of Yosys, writing synthesis scripts for a wide range of applications, creating Yosys scripts for various -non-synthesis applications (such as formal equivialence checking) and +non-synthesis applications (such as formal equivalence checking) and writing extensions to Yosys using the C++ API. \end{frame} diff --git a/manual/weblinks.bib b/manual/weblinks.bib index 5215a6ca3..d5f83315d 100644 --- a/manual/weblinks.bib +++ b/manual/weblinks.bib @@ -1,134 +1,134 @@ -
-@misc{YosysGit,
- author = {Clifford Wolf},
- title = {{Yosys Open SYnthesis Suite (YOSYS)}},
- note = {\url{http://github.com/cliffordwolf/yosys}}
-}
-
-@misc{YosysTestsGit,
- author = {Clifford Wolf},
- title = {{Yosys Test Bench}},
- note = {\url{http://github.com/cliffordwolf/yosys-tests}}
-}
-
-@misc{VlogHammer,
- author = {Clifford Wolf},
- title = {{VlogHammer Verilog Synthesis Regression Tests}},
- note = {\url{http://github.com/cliffordwolf/VlogHammer}}
-}
-
-@misc{Icarus,
- author = {Stephen Williams},
- title = {{Icarus Verilog}},
- note = {Version 0.8.7, \url{http://iverilog.icarus.com/}}
-}
-
-@misc{VTR,
- author= {Jonathan Rose and Jason Luu and Chi Wai Yu and Opal Densmore and Jeff Goeders and Andrew Somerville and Kenneth B. Kent and Peter Jamieson and Jason Anderson},
- title = {{The Verilog-to-Routing (VTR) Project for FPGAs}},
- note = {Version 1.0, \url{https://code.google.com/p/vtr-verilog-to-routing/}}
-}
-
-@misc{HANA,
- author = {Parvez Ahmad},
- title = {{HDL Analyzer and Netlist Architect (HANA)}},
- note = {Verison linux64-1.0-alpha (2012-10-14), \url{http://sourceforge.net/projects/sim-sim/}}
-}
-
-@misc{MVSIS,
- author = {MVSIS group at Berkeley studies logic synthesis and verification for VLSI design},
- title = {{MVSIS: Logic Synthesis and Verification}},
- note = {Version 3.0, \url{http://embedded.eecs.berkeley.edu/mvsis/}}
-}
-
-@misc{VIS,
- author = {{The VIS group}},
- title = {{VIS: A system for Verification and Synthesis}},
- note = {Version 2.4, \url{http://vlsi.colorado.edu/~vis/}}
-}
-
-@misc{ABC,
- author = {{Berkeley Logic Synthesis and Verification Group}},
- title = {{ABC: A System for Sequential Synthesis and Verification}},
- note = {HQ Rev b5750272659f, 2012-10-28, \url{http://www.eecs.berkeley.edu/~alanmi/abc/}}
-}
-
-@misc{AIGER,
- author = {{Armin Biere, Johannes Kepler University Linz, Austria}},
- title = {{AIGER}},
- note = {\url{http://fmv.jku.at/aiger/}}
-}
-
-@misc{XilinxWebPACK,
- author = {{Xilinx, Inc.}},
- title = {{ISE WebPACK Design Software}},
- note = {\url{http://www.xilinx.com/products/design-tools/ise-design-suite/ise-webpack.htm}}
-}
-
-@misc{QuartusWeb,
- author = {{Altera, Inc.}},
- title = {{Quartus II Web Edition Software}},
- note = {\url{http://www.altera.com/products/software/quartus-ii/web-edition/qts-we-index.html}}
-}
-
-@misc{OR1200,
- title = {{OpenRISC 1200 CPU}},
- note = {\url{http://opencores.org/or1k/OR1200\_OpenRISC\_Processor}}
-}
-
-@misc{openMSP430,
- title = {{openMSP430 CPU}},
- note = {\url{http://opencores.org/project,openmsp430}}
-}
-
-@misc{i2cmaster,
- title = {{OpenCores I$^2$C Core}},
- note = {\url{http://opencores.org/project,i2c}}
-}
-
-@misc{k68,
- title = {{OpenCores k68 Core}},
- note = {\url{http://opencores.org/project,k68}}
-}
-
-@misc{bison,
- title = {{GNU Bison}},
- note = {\url{http://www.gnu.org/software/bison/}}
-}
-
-@misc{flex,
- title = {{Flex}},
- note = {\url{http://flex.sourceforge.net/}}
-}
-
-@misc{C_to_Verilog,
- title = {{C-to-Verilog}},
- note = {\url{http://www.c-to-verilog.com/}}
-}
-
-@misc{LegUp,
- title = {{LegUp}},
- note = {\url{http://legup.eecg.utoronto.ca/}}
-}
-
-@misc{LibertyFormat,
- title = {{The Liberty Library Modeling Standard}},
- note = {\url{http://www.opensourceliberty.org/}}
-}
-
-@misc{ASIC-WORLD,
- title = {{World of ASIC}},
- note = {\url{http://www.asic-world.com/}}
-}
-
-@misc{Formality,
- title = {{Synopsys Formality Equivalence Checking}},
- note = {\url{http://www.synopsys.com/Tools/Verification/FormalEquivalence/Pages/Formality.aspx}},
-}
-
-@misc{bigint,
- author = {Matt McCutchen},
- title = {{C++ Big Integer Library}},
- note = {\url{http://mattmccutchen.net/bigint/}}
-}
-
+ +@misc{YosysGit, + author = {Clifford Wolf}, + title = {{Yosys Open SYnthesis Suite (YOSYS)}}, + note = {\url{http://github.com/cliffordwolf/yosys}} +} + +@misc{YosysTestsGit, + author = {Clifford Wolf}, + title = {{Yosys Test Bench}}, + note = {\url{http://github.com/cliffordwolf/yosys-tests}} +} + +@misc{VlogHammer, + author = {Clifford Wolf}, + title = {{VlogHammer Verilog Synthesis Regression Tests}}, + note = {\url{http://github.com/cliffordwolf/VlogHammer}} +} + +@misc{Icarus, + author = {Stephen Williams}, + title = {{Icarus Verilog}}, + note = {Version 0.8.7, \url{http://iverilog.icarus.com/}} +} + +@misc{VTR, + author= {Jonathan Rose and Jason Luu and Chi Wai Yu and Opal Densmore and Jeff Goeders and Andrew Somerville and Kenneth B. Kent and Peter Jamieson and Jason Anderson}, + title = {{The Verilog-to-Routing (VTR) Project for FPGAs}}, + note = {Version 1.0, \url{https://code.google.com/p/vtr-verilog-to-routing/}} +} + +@misc{HANA, + author = {Parvez Ahmad}, + title = {{HDL Analyzer and Netlist Architect (HANA)}}, + note = {Verison linux64-1.0-alpha (2012-10-14), \url{http://sourceforge.net/projects/sim-sim/}} +} + +@misc{MVSIS, + author = {MVSIS group at Berkeley studies logic synthesis and verification for VLSI design}, + title = {{MVSIS: Logic Synthesis and Verification}}, + note = {Version 3.0, \url{http://embedded.eecs.berkeley.edu/mvsis/}} +} + +@misc{VIS, + author = {{The VIS group}}, + title = {{VIS: A system for Verification and Synthesis}}, + note = {Version 2.4, \url{http://vlsi.colorado.edu/~vis/}} +} + +@misc{ABC, + author = {{Berkeley Logic Synthesis and Verification Group}}, + title = {{ABC: A System for Sequential Synthesis and Verification}}, + note = {HQ Rev b5750272659f, 2012-10-28, \url{http://www.eecs.berkeley.edu/~alanmi/abc/}} +} + +@misc{AIGER, + author = {{Armin Biere, Johannes Kepler University Linz, Austria}}, + title = {{AIGER}}, + note = {\url{http://fmv.jku.at/aiger/}} +} + +@misc{XilinxWebPACK, + author = {{Xilinx, Inc.}}, + title = {{ISE WebPACK Design Software}}, + note = {\url{http://www.xilinx.com/products/design-tools/ise-design-suite/ise-webpack.htm}} +} + +@misc{QuartusWeb, + author = {{Altera, Inc.}}, + title = {{Quartus II Web Edition Software}}, + note = {\url{http://www.altera.com/products/software/quartus-ii/web-edition/qts-we-index.html}} +} + +@misc{OR1200, + title = {{OpenRISC 1200 CPU}}, + note = {\url{http://opencores.org/or1k/OR1200\_OpenRISC\_Processor}} +} + +@misc{openMSP430, + title = {{openMSP430 CPU}}, + note = {\url{http://opencores.org/project,openmsp430}} +} + +@misc{i2cmaster, + title = {{OpenCores I$^2$C Core}}, + note = {\url{http://opencores.org/project,i2c}} +} + +@misc{k68, + title = {{OpenCores k68 Core}}, + note = {\url{http://opencores.org/project,k68}} +} + +@misc{bison, + title = {{GNU Bison}}, + note = {\url{http://www.gnu.org/software/bison/}} +} + +@misc{flex, + title = {{Flex}}, + note = {\url{http://flex.sourceforge.net/}} +} + +@misc{C_to_Verilog, + title = {{C-to-Verilog}}, + note = {\url{http://www.c-to-verilog.com/}} +} + +@misc{LegUp, + title = {{LegUp}}, + note = {\url{http://legup.eecg.utoronto.ca/}} +} + +@misc{LibertyFormat, + title = {{The Liberty Library Modeling Standard}}, + note = {\url{http://www.opensourceliberty.org/}} +} + +@misc{ASIC-WORLD, + title = {{World of ASIC}}, + note = {\url{http://www.asic-world.com/}} +} + +@misc{Formality, + title = {{Synopsys Formality Equivalence Checking}}, + note = {\url{http://www.synopsys.com/Tools/Verification/FormalEquivalence/Pages/Formality.aspx}}, +} + +@misc{bigint, + author = {Matt McCutchen}, + title = {{C++ Big Integer Library}}, + note = {\url{http://mattmccutchen.net/bigint/}} +} + diff --git a/misc/yosysjs/demo01.html b/misc/yosysjs/demo01.html new file mode 100644 index 000000000..3f9f737e9 --- /dev/null +++ b/misc/yosysjs/demo01.html @@ -0,0 +1,197 @@ +<html><head> + <title>YosysJS Example Application #01</title> + <script type="text/javascript" src="yosysjs.js"></script> +</head><body onload="document.getElementById('command').focus()"> + <h1>YosysJS Example Application #01</h1> + <table width="100%"><tr><td><div id="tabs"></div></td><td align="right"><tt>[ <span onclick="load_example()">load example</span> ]</tt></td></tr></table> + <svg id="svg" style="display: none; position: absolute; padding: 10px; width: calc(100% - 40px); height: 480px;"></svg> + <div><textarea id="output" style="width: 100%; height: 500px"></textarea></div> + <div id="wait" style="display: block"><br/><b><span id="waitmsg">Loading...</span></b></div> + <div id="input" style="display: none"><form onsubmit="window.setTimeout(run_command); return false"><br/><tt><span id="prompt"> + </span></tt><input id="command" type="text" onkeydown="history(event)" style="font-family: monospace; font-weight: bold;" size="100"></form></div> + <script type='text/javascript'> + function print_output(text) { + var el = document.getElementById('output'); + el.value += text + "\n"; + } + + YosysJS.load_viz(); + var ys = YosysJS.create("", function() { + print_output(ys.print_buffer); + + document.getElementById('wait').style.display = 'none'; + document.getElementById('input').style.display = 'block'; + document.getElementById('waitmsg').textContent = 'Waiting for yosys.js...'; + document.getElementById('prompt').textContent = ys.prompt(); + + update_tabs(); + }); + + ys.echo = true; + + var history_log = []; + var history_index = 0; + var history_bak = ""; + + function history(ev) { + if (ev.keyCode == 38) { + el = document.getElementById('command'); + if (history_index == history_log.length) + history_bak = el.value + if (history_index > 0) + el.value = history_log[--history_index]; + } + if (ev.keyCode == 40) { + if (history_index < history_log.length) { + el = document.getElementById('command'); + if (++history_index < history_log.length) + el.value = history_log[history_index]; + else + el.value = history_bak; + } + } + } + + var current_file = ""; + var console_messages = ""; + var svg_cache = { }; + + function update_tabs() { + var f, html = "", flist = ys.read_dir('.'); + if (current_file == "") { + html += '<tt>[ <b>Console</b>'; + } else { + html += '<tt>[ <span onclick="open_file(\'\')">Console</span>'; + } + for (i in flist) { + f = flist[i] + if (f == "." || f == "..") + continue; + if (current_file == f) { + html += ' | <b>' + f + '</b>'; + } else { + html += ' | <span onclick="open_file(\'' + f + '\')">' + f + '</span>'; + } + } + html += ' | <span onclick="open_file(prompt(\'Filename:\'))">new file</span> ]</tt>'; + document.getElementById('tabs').innerHTML = html; + if (current_file == "" || /\.dot$/.test(current_file)) { + var element = document.getElementById('output'); + element.readOnly = true; + element.scrollTop = element.scrollHeight; // focus on bottom + document.getElementById('command').focus(); + } else { + document.getElementById('output').readOnly = false; + document.getElementById('output').focus(); + } + } + + function open_file(filename) + { + if (current_file == "") + console_messages = document.getElementById('output').value; + else if (!/\.dot$/.test(current_file)) + ys.write_file(current_file, document.getElementById('output').value); + + if (filename == "") { + document.getElementById('output').value = console_messages; + } else { + try { + document.getElementById('output').value = ys.read_file(filename); + } catch (e) { + document.getElementById('output').value = ""; + ys.write_file(filename, document.getElementById('output').value); + } + } + + if (/\.dot$/.test(filename)) { + dot = document.getElementById('output').value; + YosysJS.dot_into_svg(dot, 'svg'); + document.getElementById('svg').style.display = 'block'; + document.getElementById('output').value = ''; + } else { + document.getElementById('svg').innerHTML = ''; + document.getElementById('svg').style.display = 'none'; + } + + current_file = filename; + update_tabs() + } + + function startup() { + } + + function load_example() { + open_file('') + + var txt = ""; + txt += "// a simple yosys.js example. run \"script example.ys\".\n"; + txt += "\n"; + txt += "module example(input clk, input rst, input inc, output reg [3:0] cnt);\n"; + txt += " always @(posedge clk) begin\n"; + txt += " if (rst)\n"; + txt += " cnt <= 0;\n"; + txt += " else if (inc)\n"; + txt += " cnt <= cnt + 1;\n"; + txt += " end\n"; + txt += "endmodule\n"; + txt += "\n"; + ys.write_file('example.v', txt); + + var txt = ""; + txt += "# a simple yosys.js example. run \"script example.ys\".\n"; + txt += "\n"; + txt += "design -reset\n"; + txt += "read_verilog example.v\n"; + txt += "proc\n"; + txt += "opt\n"; + txt += "show\n"; + txt += "\n"; + ys.write_file('example.ys', txt); + + open_file('example.ys') + document.getElementById('command').focus(); + } + + function run_command() { + var cmd = document.getElementById('command').value; + document.getElementById('command').value = ''; + if (history_log.length == 0 || history_log[history_log.length-1] != cmd) + history_log.push(cmd); + history_index = history_log.length; + + var show_dot_before = ""; + try { show_dot_before = ys.read_file('show.dot'); } catch (e) { } + + open_file(''); + + document.getElementById('wait').style.display = 'block'; + document.getElementById('input').style.display = 'none'; + + function run_command_bh() { + try { + ys.run(cmd); + } catch (e) { + ys.write('Caught JavaScript exception. (see JavaScript console for details.)'); + console.log(e); + } + print_output(ys.print_buffer); + + document.getElementById('wait').style.display = 'none'; + document.getElementById('input').style.display = 'block'; + document.getElementById('prompt').textContent = ys.prompt(); + + var show_dot_after = ""; + try { show_dot_after = ys.read_file('show.dot'); } catch (e) { } + + if (show_dot_before != show_dot_after) + open_file('show.dot'); + + update_tabs(); + } + + window.setTimeout(run_command_bh, 50); + return false; + } + </script> +</body></html> diff --git a/misc/yosysjs/demo02.html b/misc/yosysjs/demo02.html new file mode 100644 index 000000000..9191db98d --- /dev/null +++ b/misc/yosysjs/demo02.html @@ -0,0 +1,103 @@ +<html><head> + <title>YosysJS Example Application #02</title> + <script type="text/javascript" src="yosysjs.js"></script> +</head><body> + <div id="popup" style="position: fixed; left: 0; top: 0; width:100%; height:100%; text-align:center; z-index: 1000;"><div + style="width:300px; margin: 200px auto; background-color: #88f; border:3px dashed #000; + padding:15px; text-align:center;"><span id="popupmsg">Loading...</span></div> + </div> + <h1>YosysJS Example Application #02</h1> + <textarea id="code" style="width: 800px; height: 300px;"> +// borrowed with some modifications from +// http://www.ee.ed.ac.uk/~gerard/Teach/Verilog/manual/Example/lrgeEx2/cooley.html +module up3down5(clock, data_in, up, down, carry_out, borrow_out, count_out, parity_out); + +input [8:0] data_in; +input clock, up, down; + +output reg [8:0] count_out; +output reg carry_out, borrow_out, parity_out; + +reg [9:0] cnt_up, cnt_dn; +reg [8:0] count_nxt; + +always @(posedge clock) +begin + cnt_dn = count_out - 3'b 101; + cnt_up = count_out + 2'b 11; + + case ({up,down}) + 2'b 00 : count_nxt = data_in; + 2'b 01 : count_nxt = cnt_dn; + 2'b 10 : count_nxt = cnt_up; + 2'b 11 : count_nxt = count_out; + default : count_nxt = 9'bX; + endcase + + parity_out <= ^count_nxt; + carry_out <= up & cnt_up[9]; + borrow_out <= down & cnt_dn[9]; + count_out <= count_nxt; +end + +endmodule + </textarea><p/> + <input type="button" value="Before Behavioral Synth" onclick="synth1()"> + <input type="button" value="After Behavioral Synth" onclick="synth2()"> + <input type="button" value="After RTL Synth" onclick="synth3()"> + <input type="button" value="After Gate-Level Synth" onclick="synth4()"><p/> + <svg id="svg" width="800"></svg> + </td></tr></table> + <script type="text/javascript"> + YosysJS.load_viz(); + function on_ys_ready() { + document.getElementById('popup').style.visibility = 'hidden'; + document.getElementById('popupmsg').textContent = 'Please wait..'; + } + function handle_run_errors(logmsg, errmsg) { + if (errmsg != "") { + window.alert(errmsg); + document.getElementById('popup').style.visibility = 'hidden'; + } + } + function synth1() { + document.getElementById('popup').style.visibility = 'visible'; + ys.write_file("input.v", document.getElementById('code').value); + ys.run('design -reset; read_verilog input.v; show -stretch', handle_run_errors); + ys.read_file('show.dot', (function(text){ + console.log(ys.errmsg); + if (ys.errmsg == "") YosysJS.dot_into_svg(text, 'svg'); + document.getElementById('popup').style.visibility = 'hidden'; + })); + } + function synth2() { + document.getElementById('popup').style.visibility = 'visible'; + ys.write_file("input.v", document.getElementById('code').value); + ys.run('design -reset; read_verilog input.v; proc; opt_clean; show -stretch', handle_run_errors); + ys.read_file('show.dot', (function(text){ + if (ys.errmsg == "") YosysJS.dot_into_svg(text, 'svg'); + document.getElementById('popup').style.visibility = 'hidden'; + })); + } + function synth3() { + document.getElementById('popup').style.visibility = 'visible'; + ys.write_file("input.v", document.getElementById('code').value); + ys.run('design -reset; read_verilog input.v; synth -run coarse; show -stretch', handle_run_errors); + ys.read_file('show.dot', (function(text){ + if (ys.errmsg == "") YosysJS.dot_into_svg(text, 'svg'); + document.getElementById('popup').style.visibility = 'hidden'; + })); + } + function synth4() { + document.getElementById('popup').style.visibility = 'visible'; + ys.write_file("input.v", document.getElementById('code').value); + ys.run('design -reset; read_verilog input.v; synth -run coarse; synth -run fine; show -stretch', handle_run_errors); + ys.read_file('show.dot', (function(text){ + if (ys.errmsg == "") YosysJS.dot_into_svg(text, 'svg'); + document.getElementById('popup').style.visibility = 'hidden'; + })); + } + var ys = YosysJS.create_worker(on_ys_ready); + ys.verbose(true); + </script> +</body></html> diff --git a/misc/yosysjs/demo03.html b/misc/yosysjs/demo03.html new file mode 100644 index 000000000..3dc465cbf --- /dev/null +++ b/misc/yosysjs/demo03.html @@ -0,0 +1,103 @@ +<html><head> +<title>YosysJS Example Application #02</title> +<script type="text/javascript" src="yosysjs.js"></script> +<script src="http://wavedrom.com/skins/default.js" type="text/javascript"></script> +<script src="http://wavedrom.com/WaveDrom.js" type="text/javascript"></script> +<style type="text/css"> +.noedit { color: #666; } +</style> +<script id="golden_verilog" type="text/plain"> +module ref(input clk, reset, input [7:0] A, output reg [7:0] Y); + always @(posedge clk) begin + if (reset) + Y <= 0; + else + Y <= ((Y << 5) + Y) ^ A; + end +endmodule +</script> +</head><body> + <div id="popup" style="position: fixed; left: 0; top: 0; width:100%; height:100%; text-align:center; z-index: 1000; + background-color: rgba(100, 100, 100, 0.5);"><div style="width:300px; margin: 200px auto; background-color: #88f; + border:3px dashed #000; padding:15px; text-align:center;"><span id="popupmsg">Loading...</span></div> + </div> + <h1>YosysJS Example Application #03</h1> + <b>Your mission:</b> Create a behavioral Verilog model for the following circuit: + <p/> + <div id="main" style="visibility: hidden"> + <svg id="schem" width="800"></svg> + <p/> + <pre id="code" style="width: 800px; border:2px solid #000; padding: 0.5em;"><span class="noedit">module top(input clk, reset, input [7:0] A, output reg [7:0] Y); + always @(posedge clock) begin</span><span class="edit" contenteditable="true"> + Y <= A | {4{reset}}; + </span><span class="noedit">end +endmodule</span></pre><p/> + <input type="button" value="Check Model" onclick="check_model()"> <span id="checkmessage"></span> + <p/> + <p id="wave"> </p> + </div> + <script type="text/javascript"> + function on_ys_ready() { + ys.write_file('golden.v', document.getElementById('golden_verilog').textContent); + ys.run('echo on; read_verilog golden.v; proc;;'); + ys.run('show -notitle -width -stretch'); + YosysJS.dot_into_svg(ys.read_file('show.dot'), 'schem'); + document.getElementById('popup').style.visibility = 'hidden'; + document.getElementById('popupmsg').textContent = 'Please wait..'; + document.getElementById('main').style.visibility = 'visible'; + } + function check_model() { + function work() { + ys.remove_file('wave.json'); + ys.write_file('code.v', document.getElementById('code').textContent); + ys.errmsg = ''; + ys.run('design -reset; read_verilog code.v; hierarchy -top top; proc; opt; flatten; hierarchy; ' + + 'read_verilog golden.v; proc; miter -equiv -ignore_gold_x -make_outputs -flatten ref top miter; ' + + 'hierarchy -top miter; clean -purge; sat -set-init-undef -seq 8 -dump_json wave.json -show-ports ' + + '-max_undef -prove trigger 0 miter'); + w = document.getElementById('wave') + if (ys.errmsg) { + w.innerHTML = '<b><pre>ERROR: ' + ys.errmsg.replace('&', '&').replace('<', '<').replace('>', '>') + '</pre></b>'; + } else { + wdata = ys.read_file('wave.json'); + if (wdata) { + wdata = JSON.parse(wdata); + function wsignal(signame, newname) { + for (i = 0; i < wdata["signal"].length; i++) + if (wdata["signal"][i].name == signame) { + if (newname) + wdata["signal"][i].name = newname; + return wdata["signal"][i]; + } + return {}; + } + wdata2 = { + "signal" : [ + { name: 'clk', wave: 'P........' }, + wsignal("trigger"), + {}, + [ "Inputs", wsignal("in_reset", "reset"), wsignal("in_A", "A") ], + {}, + [ "Y Output", wsignal("gold_Y", "Ref"), wsignal("gate_Y", "UUT") ], + ], + "config" : wdata["config"] + }; + wdata2 = JSON.stringify(wdata2) + w.innerHTML = '<b>The model did not pass verification:</b><p/>' + + '<script type="WaveDrom">' + wdata2 + '<\/script>'; + WaveDrom.ProcessAll(); + } else { + w.innerHTML = '<b>Congratulations! The model did pass verification.</b><p/>'; + } + } + document.getElementById('popup').style.visibility = 'hidden'; + } + document.getElementById('popup').style.visibility = 'visible'; + window.setTimeout(work, 100); + } + + YosysJS.load_viz(); + var ys = YosysJS.create('', on_ys_ready); + ys.logprint = true; + </script> +</body></html> diff --git a/misc/yosysjs/yosysjs.js b/misc/yosysjs/yosysjs.js new file mode 100644 index 000000000..ea98c9b51 --- /dev/null +++ b/misc/yosysjs/yosysjs.js @@ -0,0 +1,312 @@ +var YosysJS = new function() { + this.script_element = document.currentScript; + this.viz_element = undefined; + this.viz_ready = true; + + this.url_prefix = this.script_element.src.replace(/[^/]+$/, '') + + this.load_viz = function() { + if (this.viz_element) + return; + + this.viz_element = document.createElement('iframe') + this.viz_element.style.display = 'none' + document.body.appendChild(this.viz_element); + + this.viz_element.contentWindow.document.open(); + this.viz_element.contentWindow.document.write('<script type="text/javascript" onload="viz_ready = true;" src="' + this.url_prefix + 'viz.js"></' + 'script>'); + this.viz_element.contentWindow.document.close(); + + var that = this; + function check_viz_ready() { + if (that.viz_element.contentWindow.viz_ready) { + console.log("YosysJS: Successfully loaded Viz."); + that.viz_ready = true; + } else + window.setTimeout(check_viz_ready, 100); + } + + this.viz_ready = false; + window.setTimeout(check_viz_ready, 100); + } + + this.dot_to_svg = function(dot_text) { + return this.viz_element.contentWindow.Viz(dot_text, "svg"); + } + + this.dot_into_svg = function(dot_text, svg_element) { + if (typeof(svg_element) == 'string' && svg_element != "") + svg_element = document.getElementById(svg_element); + svg_element.innerHTML = this.dot_to_svg(dot_text); + c = svg_element.firstChild; + while (c) { + if (c.tagName == 'svg') { + while (c.firstChild) + svg_element.appendChild(c.firstChild); + svg_element.setAttribute('viewBox', c.getAttribute('viewBox')); + // svg_element.removeChild(c); + break; + } + c = c.nextSibling; + } + } + + this.create = function(reference_element, on_ready) { + var ys = new Object(); + ys.YosysJS = this; + ys.init_script = ""; + ys.ready = false; + ys.verbose = false; + ys.logprint = false; + ys.echo = false; + ys.errmsg = ""; + + if (typeof(reference_element) == 'string' && reference_element != "") + reference_element = document.getElementById(reference_element); + + if (reference_element) { + if (reference_element.tagName == 'textarea') + ys.init_script = reference_element.value; + + if (reference_element.tagName == 'iframe') { + ys.iframe_element = reference_element; + } else { + ys.iframe_element = document.createElement('iframe'); + ys.iframe_element.id = reference_element.id; + for (i in reference_element.style) + ys.iframe_element.style[i] = reference_element.style[i]; + reference_element.parentNode.insertBefore(ys.iframe_element, reference_element); + reference_element.parentNode.removeChild(reference_element); + } + } else { + ys.iframe_element = document.createElement('iframe'); + ys.iframe_element.style.display = 'none'; + document.body.appendChild(ys.iframe_element); + } + + ys.print_buffer = ""; + ys.last_line_empty = false; + ys.got_normal_log_message = false; + ys.window = ys.iframe_element.contentWindow; + + var doc = ys.window.document; + var mod = ys.window.Module = { + print: function(text) { + if (typeof(text) == 'number') + return; + ys.print_buffer += text + "\n"; + ys.got_normal_log_message = true; + if (ys.logprint) + console.log(text); + if (ys.verbose) { + ys.last_line_empty = text == ""; + if (text == "") { + span = doc.createElement('br'); + } else { + span = doc.createElement('span'); + span.textContent = text + "\n"; + span.style.fontFamily = 'monospace'; + span.style.whiteSpace = 'pre'; + } + doc.firstChild.appendChild(span); + if (doc.body) + ys.window.scrollTo(0, doc.body.scrollHeight); + else + ys.window.scrollBy(0, 100); + } + ys.ready = true; + }, + printErr: function(text) { + if (typeof(text) == 'number') + return; + if (ys.logprint) + console.log(text); + if (ys.got_normal_log_message) { + ys.print_buffer += text + "\n"; + ys.last_line_empty = text == ""; + if (text == "") { + span = doc.createElement('br'); + } else { + span = doc.createElement('span'); + span.textContent = text + "\n"; + span.style.fontFamily = 'monospace'; + span.style.whiteSpace = 'pre'; + span.style.color = 'red'; + } + doc.firstChild.appendChild(span); + if (doc.body) + ys.window.scrollTo(0, doc.body.scrollHeight); + else + ys.window.scrollBy(0, 100); + } else + if (!ys.logprint) + console.log(text); + }, + }; + + ys.write = function(text) { + ys.print_buffer += text + "\n"; + ys.last_line_empty = text == ""; + span = doc.createElement('span'); + span.textContent = text + "\n"; + span.style.fontFamily = 'monospace'; + span.style.whiteSpace = 'pre'; + doc.firstChild.appendChild(span); + if (doc.body) + ys.window.scrollTo(0, doc.body.scrollHeight); + else + ys.window.scrollBy(0, 100); + } + + ys.prompt = function() { + return mod.ccall('prompt', 'string', [], []) + } + + ys.run = function(cmd) { + ys.print_buffer = ""; + if (ys.echo) { + if (!ys.last_line_empty) + ys.write(""); + ys.write(ys.prompt() + cmd); + } + try { + mod.ccall('run', '', ['string'], [cmd]); + } catch (e) { + ys.errmsg = mod.ccall('errmsg', 'string', [], []); + } + return ys.print_buffer; + } + + ys.read_file = function(filename) { + try { + return ys.window.FS.readFile(filename, {encoding: 'utf8'}); + } catch (e) { + return ""; + } + } + + ys.write_file = function(filename, text) { + return ys.window.FS.writeFile(filename, text, {encoding: 'utf8'}); + } + + ys.read_dir = function(dirname) { + return ys.window.FS.readdir(dirname); + } + + ys.remove_file = function(filename) { + try { + ys.window.FS.unlink(filename); + } catch (e) { } + } + + doc.open(); + doc.write('<script type="text/javascript" src="' + this.url_prefix + 'yosys.js"></' + 'script>'); + doc.close(); + + if (on_ready || ys.init_script) { + function check_ready() { + if (ys.ready && ys.YosysJS.viz_ready) { + if (ys.init_script) { + ys.write_file("/script.ys", ys.init_script); + ys.run("script /script.ys"); + } + if (on_ready) + on_ready(ys); + } else + window.setTimeout(check_ready, 100); + } + window.setTimeout(check_ready, 100); + } + + return ys; + } + + this.create_worker = function(on_ready) { + var ys = new Object(); + ys.YosysJS = this; + ys.worker = new Worker(this.url_prefix + 'yosyswrk.js'); + ys.callback_idx = 1; + ys.callback_cache = {}; + ys.errmsg = ""; + + ys.callback_cache[0] = on_ready; + on_ready = null; + + ys.worker.onmessage = function(e) { + var response = e.data[0]; + var callback = ys.callback_cache[response.idx]; + delete ys.callback_cache[response.idx]; + if ("errmsg" in response) ys.errmsg = response.errmsg; + if (callback) callback.apply(null, response.args); + } + + ys.run = function(cmd, callback) { + var request = { + "idx": ys.callback_idx, + "mode": "run", + "cmd": cmd + }; + + ys.callback_cache[ys.callback_idx++] = callback; + ys.worker.postMessage([request]); + } + + ys.read_file = function(filename, callback) { + var request = { + "idx": ys.callback_idx, + "mode": "read_file", + "filename": filename + }; + + ys.callback_cache[ys.callback_idx++] = callback; + ys.worker.postMessage([request]); + } + + ys.write_file = function(filename, text, callback) { + var request = { + "idx": ys.callback_idx, + "mode": "write_file", + "filename": filename, + "text": text + }; + + ys.callback_cache[ys.callback_idx++] = callback; + ys.worker.postMessage([request]); + } + + ys.read_dir = function(dirname, callback) { + var request = { + "idx": ys.callback_idx, + "mode": "read_dir", + "dirname": dirname + }; + + ys.callback_cache[ys.callback_idx++] = callback; + ys.worker.postMessage([request]); + } + + ys.remove_file = function(filename, callback) { + var request = { + "idx": ys.callback_idx, + "mode": "remove_file", + "filename": filename + }; + + ys.callback_cache[ys.callback_idx++] = callback; + ys.worker.postMessage([request]); + } + + ys.verbose = function(value, callback) { + var request = { + "idx": ys.callback_idx, + "mode": "verbose", + "value": value + }; + + ys.callback_cache[ys.callback_idx++] = callback; + ys.worker.postMessage([request]); + } + + return ys; + } +} diff --git a/misc/yosysjs/yosyswrk.js b/misc/yosysjs/yosyswrk.js new file mode 100644 index 000000000..b6173439f --- /dev/null +++ b/misc/yosysjs/yosyswrk.js @@ -0,0 +1,63 @@ +var Module = {}; +var verbose_mode = false; +var text_buffer = ""; + +Module["printErr"] = Module["print"] = function(text) { + if (verbose_mode) + console.log(text); + text_buffer += text + "\n"; +} + +importScripts('yosys.js'); + +onmessage = function(e) { + var request = e.data[0]; + var response = { "idx": request.idx, "args": [] }; + + if (request.mode == "run") { + response["errmsg"] = ""; + try { + text_buffer = ""; + Module.ccall('run', '', ['string'], [request.cmd]); + } catch (e) { + response.errmsg = Module.ccall('errmsg', 'string', [], []); + } + response.args.push(text_buffer); + response.args.push(response.errmsg); + text_buffer = ""; + } + + if (request.mode == "read_file") { + try { + response.args.push(FS.readFile(request.filename, {encoding: 'utf8'})); + } catch (e) { } + } + + if (request.mode == "write_file") { + try { + FS.writeFile(request.filename, request.text, {encoding: 'utf8'}); + } catch (e) { } + } + + if (request.mode == "read_dir") { + try { + response.args.push(FS.readdir(request.dirname)); + } catch (e) { } + } + + if (request.mode == "remove_file") { + try { + FS.unlink(request.filename); + } catch (e) { } + } + + if (request.mode == "verbose") { + if (request.value) + console.log(text_buffer); + verbose_mode = request.value; + } + + postMessage([response]); +} + +postMessage([{ "idx": 0, "args": [] }]); diff --git a/passes/abc/Makefile.inc b/passes/abc/Makefile.inc deleted file mode 100644 index a7c5b02c6..000000000 --- a/passes/abc/Makefile.inc +++ /dev/null @@ -1,6 +0,0 @@ - -ifeq ($(ENABLE_ABC),1) -OBJS += passes/abc/abc.o -OBJS += passes/abc/blifparse.o -endif - diff --git a/passes/abc/blifparse.cc b/passes/abc/blifparse.cc deleted file mode 100644 index db87eec4a..000000000 --- a/passes/abc/blifparse.cc +++ /dev/null @@ -1,244 +0,0 @@ -/* - * 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 "blifparse.h" - -YOSYS_NAMESPACE_BEGIN - -static bool read_next_line(char *&buffer, size_t &buffer_size, int &line_count, FILE *f) -{ - int buffer_len = 0; - buffer[0] = 0; - - while (1) - { - buffer_len += strlen(buffer + buffer_len); - while (buffer_len > 0 && (buffer[buffer_len-1] == ' ' || buffer[buffer_len-1] == '\t' || - buffer[buffer_len-1] == '\r' || buffer[buffer_len-1] == '\n')) - buffer[--buffer_len] = 0; - - if (buffer_size-buffer_len < 4096) { - buffer_size *= 2; - buffer = (char*)realloc(buffer, buffer_size); - } - - if (buffer_len == 0 || buffer[buffer_len-1] == '\\') { - if (buffer_len > 0 && buffer[buffer_len-1] == '\\') - buffer[--buffer_len] = 0; - line_count++; - if (fgets(buffer+buffer_len, buffer_size-buffer_len, f) == NULL) - return false; - } else - return true; - } -} - -RTLIL::Design *abc_parse_blif(FILE *f, std::string dff_name) -{ - RTLIL::Design *design = new RTLIL::Design; - RTLIL::Module *module = new RTLIL::Module; - - RTLIL::Const *lutptr = NULL; - RTLIL::State lut_default_state = RTLIL::State::Sx; - - module->name = "\\netlist"; - design->add(module); - - size_t buffer_size = 4096; - char *buffer = (char*)malloc(buffer_size); - int line_count = 0; - - while (1) - { - if (!read_next_line(buffer, buffer_size, line_count, f)) - goto error; - - continue_without_read: - if (buffer[0] == '#') - continue; - - if (buffer[0] == '.') - { - if (lutptr) { - for (auto &bit : lutptr->bits) - if (bit == RTLIL::State::Sx) - bit = lut_default_state; - lutptr = NULL; - lut_default_state = RTLIL::State::Sx; - } - - char *cmd = strtok(buffer, " \t\r\n"); - - if (!strcmp(cmd, ".model")) - continue; - - if (!strcmp(cmd, ".end")) { - module->fixup_ports(); - free(buffer); - return design; - } - - 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)); - if (!strcmp(cmd, ".inputs")) - wire->port_input = true; - else - wire->port_output = true; - } - continue; - } - - if (!strcmp(cmd, ".latch")) - { - char *d = strtok(NULL, " \t\r\n"); - char *q = strtok(NULL, " \t\r\n"); - - 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)); - - RTLIL::Cell *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))); - continue; - } - - if (!strcmp(cmd, ".gate")) - { - char *p = strtok(NULL, " \t\r\n"); - if (p == NULL) - goto error; - - IdString celltype = RTLIL::escape_id(p); - RTLIL::Cell *cell = module->addCell(NEW_ID, celltype); - - while ((p = strtok(NULL, " \t\r\n")) != NULL) { - char *q = strchr(p, '='); - if (q == NULL || !q[0] || !q[1]) - 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))); - } - continue; - } - - if (!strcmp(cmd, ".names")) - { - char *p; - RTLIL::SigSpec input_sig, output_sig; - while ((p = strtok(NULL, " \t\r\n")) != NULL) { - RTLIL::Wire *wire; - if (module->wires_.count(stringf("\\%s", p)) > 0) { - wire = module->wires_.at(stringf("\\%s", p)); - } else { - wire = module->addWire(stringf("\\%s", p)); - } - input_sig.append(wire); - } - 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) { - RTLIL::State state = RTLIL::State::Sa; - while (1) { - if (!read_next_line(buffer, buffer_size, line_count, f)) - goto error; - for (int i = 0; buffer[i]; i++) { - if (buffer[i] == ' ' || buffer[i] == '\t') - continue; - if (i == 0 && buffer[i] == '.') - goto finished_parsing_constval; - if (buffer[i] == '0') { - if (state == RTLIL::State::S1) - goto error; - state = RTLIL::State::S0; - continue; - } - if (buffer[i] == '1') { - if (state == RTLIL::State::S0) - goto error; - state = RTLIL::State::S1; - continue; - } - goto error; - } - } - finished_parsing_constval: - if (state == RTLIL::State::Sa) - state = RTLIL::State::S1; - module->connect(RTLIL::SigSig(output_sig, state)); - goto continue_without_read; - } - - RTLIL::Cell *cell = module->addCell(NEW_ID, "$lut"); - cell->parameters["\\WIDTH"] = RTLIL::Const(input_sig.size()); - cell->parameters["\\LUT"] = RTLIL::Const(RTLIL::State::Sx, 1 << input_sig.size()); - cell->setPort("\\A", input_sig); - cell->setPort("\\Y", output_sig); - lutptr = &cell->parameters.at("\\LUT"); - lut_default_state = RTLIL::State::Sx; - continue; - } - - goto error; - } - - if (lutptr == NULL) - goto error; - - char *input = strtok(buffer, " \t\r\n"); - char *output = strtok(NULL, " \t\r\n"); - - if (input == NULL || output == NULL || (strcmp(output, "0") && strcmp(output, "1"))) - goto error; - - int input_len = strlen(input); - if (input_len > 8) - goto error; - - for (int i = 0; i < (1 << input_len); i++) { - for (int j = 0; j < input_len; j++) { - char c1 = input[j]; - if (c1 != '-') { - char c2 = (i & (1 << j)) != 0 ? '1' : '0'; - if (c1 != c2) - goto try_next_value; - } - } - lutptr->bits.at(i) = !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1; - try_next_value:; - } - - lut_default_state = !strcmp(output, "0") ? RTLIL::State::S1 : RTLIL::State::S0; - } - -error: - log_error("Syntax error in line %d!\n", line_count); - // delete design; - // return NULL; -} - -YOSYS_NAMESPACE_END - diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc index 0e62abbc4..01ada7739 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 @@ -21,4 +22,7 @@ OBJS += passes/cmds/connwrappers.o OBJS += passes/cmds/cover.o OBJS += passes/cmds/trace.o OBJS += passes/cmds/plugin.o +OBJS += passes/cmds/check.o +OBJS += passes/cmds/qwp.o +OBJS += passes/cmds/edgetypes.o diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc index 054cfc1cb..e698926f9 100644 --- a/passes/cmds/add.cc +++ b/passes/cmds/add.cc @@ -2,11 +2,11 @@ * 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 @@ -150,5 +150,5 @@ struct AddPass : public Pass { } } } AddPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc new file mode 100644 index 000000000..b3622cb19 --- /dev/null +++ b/passes/cmds/check.cc @@ -0,0 +1,162 @@ +/* + * 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" +#include "kernel/celltypes.h" +#include "kernel/utils.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct CheckPass : public Pass { + CheckPass() : Pass("check", "check for obvious problems in the design") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" check [options] [selection]\n"); + log("\n"); + log("This pass identifies the following problems in the current design:\n"); + log("\n"); + log(" - combinatorial loops\n"); + log("\n"); + log(" - two or more conflicting drivers for one wire\n"); + log("\n"); + log(" - used wires that do not have a driver\n"); + log("\n"); + log("When called with -noinit then this command also checks for wires which have\n"); + log("the 'init' attribute set.\n"); + log("\n"); + log("When called with -assert then the command will produce an error if any\n"); + log("problems are found in the current design.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + int counter = 0; + bool noinit = false; + bool assert_mode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-noinit") { + noinit = true; + continue; + } + if (args[argidx] == "-assert") { + assert_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + log_header(design, "Executing CHECK pass (checking for obvious problems).\n"); + + for (auto module : design->selected_whole_modules_warn()) + { + if (module->has_processes_warn()) + continue; + + log("checking module %s..\n", log_id(module)); + + SigMap sigmap(module); + dict<SigBit, vector<string>> wire_drivers; + dict<SigBit, int> wire_drivers_count; + pool<SigBit> used_wires; + TopoSort<string> topo; + + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) { + SigSpec sig = sigmap(conn.second); + bool logic_cell = yosys_celltypes.cell_evaluable(cell->type); + if (cell->input(conn.first)) + for (auto bit : sig) + if (bit.wire) { + if (logic_cell) + topo.edge(stringf("wire %s", log_signal(bit)), + stringf("cell %s (%s)", log_id(cell), log_id(cell->type))); + used_wires.insert(bit); + } + if (cell->output(conn.first)) + for (int i = 0; i < GetSize(sig); i++) { + if (logic_cell) + topo.edge(stringf("cell %s (%s)", log_id(cell), log_id(cell->type)), + stringf("wire %s", log_signal(sig[i]))); + if (sig[i].wire) + wire_drivers[sig[i]].push_back(stringf("port %s[%d] of cell %s (%s)", + log_id(conn.first), i, log_id(cell), log_id(cell->type))); + } + if (!cell->input(conn.first) && cell->output(conn.first)) + for (auto bit : sig) + if (bit.wire) wire_drivers_count[bit]++; + } + + for (auto wire : module->wires()) { + if (wire->port_input) { + SigSpec sig = sigmap(wire); + for (int i = 0; i < GetSize(sig); i++) + wire_drivers[sig[i]].push_back(stringf("module input %s[%d]", log_id(wire), i)); + } + if (wire->port_output) + for (auto bit : sigmap(wire)) + if (bit.wire) used_wires.insert(bit); + if (wire->port_input && !wire->port_output) + for (auto bit : sigmap(wire)) + if (bit.wire) wire_drivers_count[bit]++; + if (noinit && wire->attributes.count("\\init")) { + log_warning("Wire %s.%s has an unprocessed 'init' attribute.\n", log_id(module), log_id(wire)); + counter++; + } + } + + for (auto it : wire_drivers) + if (wire_drivers_count[it.first] > 1) { + string message = stringf("multiple conflicting drivers for %s.%s:\n", log_id(module), log_signal(it.first)); + for (auto str : it.second) + message += stringf(" %s\n", str.c_str()); + log_warning("%s", message.c_str()); + counter++; + } + + for (auto bit : used_wires) + if (!wire_drivers.count(bit)) { + log_warning("Wire %s.%s is used but has no driver.\n", log_id(module), log_signal(bit)); + counter++; + } + + topo.sort(); + for (auto &loop : topo.loops) { + string message = stringf("found logic loop in module %s:\n", log_id(module)); + for (auto &str : loop) + message += stringf(" %s\n", str.c_str()); + log_warning("%s", message.c_str()); + counter++; + } + } + + log("found and reported %d problems.\n", counter); + + if (assert_mode && counter > 0) + log_error("Found %d problems in 'check -assert'.\n", counter); + } +} CheckPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/connect.cc b/passes/cmds/connect.cc index e17c1b1c3..52611cf44 100644 --- a/passes/cmds/connect.cc +++ b/passes/cmds/connect.cc @@ -2,11 +2,11 @@ * 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 @@ -49,8 +49,8 @@ struct ConnectPass : public Pass { log("\n"); log(" connect [-nomap] [-nounset] -set <lhs-expr> <rhs-expr>\n"); log("\n"); - log("Create a connection. This is equivialent to adding the statement 'assign\n"); - log("<lhs-expr> = <rhs-expr>;' to the verilog input. Per default, all existing\n"); + log("Create a connection. This is equivalent to adding the statement 'assign\n"); + log("<lhs-expr> = <rhs-expr>;' to the Verilog input. Per default, all existing\n"); log("drivers for <lhs-expr> are unconnected. This can be overwritten by using\n"); log("the -nounset option.\n"); log("\n"); @@ -185,5 +185,5 @@ struct ConnectPass : public Pass { log_cmd_error("Expected -set, -unset, or -port.\n"); } } ConnectPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/connwrappers.cc b/passes/cmds/connwrappers.cc index a65a63644..c9ab226d5 100644 --- a/passes/cmds/connwrappers.cc +++ b/passes/cmds/connwrappers.cc @@ -2,11 +2,11 @@ * 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 @@ -158,8 +158,8 @@ struct ConnwrappersPass : public Pass { log("\n"); log("Wrappers are used in coarse-grain synthesis to wrap cells with smaller ports\n"); log("in wrapper cells with a (larger) constant port size. I.e. the upper bits\n"); - log("of the wrapper outut are signed/unsigned bit extended. This command uses this\n"); - log("knowlege to rewire the inputs of the driven cells to match the output of\n"); + log("of the wrapper output are signed/unsigned bit extended. This command uses this\n"); + log("knowledge to rewire the inputs of the driven cells to match the output of\n"); log("the driving cell.\n"); log("\n"); log(" -signed <cell_type> <port_name> <width_param>\n"); @@ -198,12 +198,12 @@ struct ConnwrappersPass : public Pass { } extra_args(args, argidx, design); - log_header("Executing CONNWRAPPERS pass (connect extended ports of wrapper cells).\n"); + log_header(design, "Executing CONNWRAPPERS pass (connect extended ports of wrapper cells).\n"); for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) worker.work(design, mod_it.second); } } ConnwrappersPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/copy.cc b/passes/cmds/copy.cc index 459e5b0e7..fb863512b 100644 --- a/passes/cmds/copy.cc +++ b/passes/cmds/copy.cc @@ -2,11 +2,11 @@ * 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 @@ -55,5 +55,5 @@ struct CopyPass : public Pass { design->add(new_mod); } } CopyPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc index 5644066af..1475475c3 100644 --- a/passes/cmds/cover.cc +++ b/passes/cmds/cover.cc @@ -124,7 +124,7 @@ struct CoverPass : public Pass { extra_args(args, argidx, design); if (do_log) { - log_header("Printing code coverage counters.\n"); + log_header(design, "Printing code coverage counters.\n"); log("\n"); } diff --git a/passes/cmds/delete.cc b/passes/cmds/delete.cc index b4362887e..6d51d30e7 100644 --- a/passes/cmds/delete.cc +++ b/passes/cmds/delete.cc @@ -2,11 +2,11 @@ * 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 @@ -140,5 +140,5 @@ struct DeletePass : public Pass { } } } DeletePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc index 9f800c31f..e900e7b9c 100644 --- a/passes/cmds/design.cc +++ b/passes/cmds/design.cc @@ -2,11 +2,11 @@ * 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 @@ -80,7 +80,7 @@ struct DesignPass : public Pass { log("\n"); log(" design -copy-to <name> [-as <new_mod_name>] [selection]\n"); log("\n"); - log("Copy modules from the current design into the soecified one.\n"); + log("Copy modules from the current design into the specified one.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) diff --git a/passes/cmds/edgetypes.cc b/passes/cmds/edgetypes.cc new file mode 100644 index 000000000..7b75a009f --- /dev/null +++ b/passes/cmds/edgetypes.cc @@ -0,0 +1,106 @@ +/* + * 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 EdgetypePass : public Pass { + EdgetypePass() : Pass("edgetypes", "list all types of edges in selection") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" edgetypes [options] [selection]\n"); + log("\n"); + log("This command lists all unique types of 'edges' found in the selection. An 'edge'\n"); + log("is a 4-tuple of source and sink cell type and port name.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // if (args[argidx] == "-ltr") { + // config.ltr = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + pool<string> edge_cache; + + for (auto module : design->selected_modules()) + { + SigMap sigmap(module); + dict<SigBit, pool<tuple<IdString, IdString, int>>> bit_sources, bit_sinks; + pool<std::pair<IdString, IdString>> multibit_ports; + + for (auto cell : module->selected_cells()) + for (auto conn : cell->connections()) + { + IdString cell_type = cell->type; + IdString port_name = conn.first; + SigSpec sig = sigmap(conn.second); + + if (GetSize(sig) > 1) + multibit_ports.insert(std::pair<IdString, IdString>(cell_type, port_name)); + + for (int i = 0; i < GetSize(sig); i++) { + if (cell->output(port_name)) + bit_sources[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i)); + if (cell->input(port_name)) + bit_sinks[sig[i]].insert(tuple<IdString, IdString, int>(cell_type, port_name, i)); + } + } + + for (auto &it : bit_sources) + for (auto &source : it.second) + for (auto &sink : bit_sinks[it.first]) + { + auto source_cell_type = std::get<0>(source); + auto source_port_name = std::get<1>(source); + auto source_bit_index = std::get<2>(source); + + auto sink_cell_type = std::get<0>(sink); + auto sink_port_name = std::get<1>(sink); + auto sink_bit_index = std::get<2>(sink); + + string source_str = multibit_ports.count(std::pair<IdString, IdString>(source_cell_type, source_port_name)) ? + stringf("%s.%s[%d]", log_id(source_cell_type), log_id(source_port_name), source_bit_index) : + stringf("%s.%s", log_id(source_cell_type), log_id(source_port_name)); + + string sink_str = multibit_ports.count(std::pair<IdString, IdString>(sink_cell_type, sink_port_name)) ? + stringf("%s.%s[%d]", log_id(sink_cell_type), log_id(sink_port_name), sink_bit_index) : + stringf("%s.%s", log_id(sink_cell_type), log_id(sink_port_name)); + + edge_cache.insert(source_str + " " + sink_str); + } + } + + edge_cache.sort(); + for (auto &str : edge_cache) + log("%s\n", str.c_str()); + } +} EdgetypePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc index f7c65bbde..828c671de 100644 --- a/passes/cmds/plugin.cc +++ b/passes/cmds/plugin.cc @@ -31,19 +31,23 @@ std::map<std::string, std::string> loaded_plugin_aliases; #ifdef YOSYS_ENABLE_PLUGINS void load_plugin(std::string filename, std::vector<std::string> aliases) { + std::string orig_filename = filename; + if (filename.find('/') == std::string::npos) filename = "./" + filename; if (!loaded_plugins.count(filename)) { void *hdl = dlopen(filename.c_str(), RTLD_LAZY|RTLD_LOCAL); + if (hdl == NULL && orig_filename.find('/') == std::string::npos) + hdl = dlopen((proc_share_dirname() + "plugins/" + orig_filename + ".so").c_str(), RTLD_LAZY|RTLD_LOCAL); if (hdl == NULL) log_cmd_error("Can't load module `%s': %s\n", filename.c_str(), dlerror()); - loaded_plugins[filename] = hdl; + loaded_plugins[orig_filename] = hdl; Pass::init_register(); } for (auto &alias : aliases) - loaded_plugin_aliases[alias] = filename; + loaded_plugin_aliases[alias] = orig_filename; } #else void load_plugin(std::string, std::vector<std::string>) @@ -115,7 +119,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 new file mode 100644 index 000000000..1b800b6df --- /dev/null +++ b/passes/cmds/qwp.cc @@ -0,0 +1,870 @@ +/* + * 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" + +#undef LOG_MATRICES +#undef PYPLOT_EDGES + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +static uint32_t xorshift32_state; + +static double xorshift32() +{ + xorshift32_state ^= xorshift32_state << 13; + xorshift32_state ^= xorshift32_state >> 17; + xorshift32_state ^= xorshift32_state << 5; + return (xorshift32_state % 1000000) / 1e6; +} + +struct QwpConfig +{ + bool ltr; + bool alpha; + bool verbose; + double grid; + + std::ofstream dump_file; + + QwpConfig() { + ltr = false; + alpha = false; + verbose = false; + grid = 1.0 / 16; + } +}; + +struct QwpWorker +{ + QwpConfig &config; + Module *module; + char direction; + + struct Node { + Cell *cell; + bool tied, alt_tied; + + // pos = position in current direction + // alt_pos = position in the other direction + double pos, alt_pos; + + Node() { + cell = nullptr; + tied = false; + pos = xorshift32(); + alt_tied = false; + alt_pos = xorshift32(); + } + + void tie(double v) { + tied = true; + pos = v; + } + + void alt_tie(double v) { + alt_tied = true; + alt_pos = v; + } + + void swap_alt() { + std::swap(tied, alt_tied); + std::swap(pos, alt_pos); + } + + void proj_left(double midpos) { + cell = nullptr; + tie(pos > midpos ? midpos : pos); + } + + void proj_right(double midpos) { + cell = nullptr; + tie(pos < midpos ? midpos : pos); + } + }; + + vector<Node> nodes; + dict<pair<int, int>, double> edges; + dict<Cell*, int> cell_to_node; + + // worker state variables + double midpos; + double radius; + double alt_midpos; + double alt_radius; + + QwpWorker(QwpConfig &config, Module *module, char direction = 'x') : config(config), module(module), direction(direction) + { + log_assert(direction == 'x' || direction == 'y'); + } + + void load_module() + { + log_assert(direction == 'x'); + + SigMap sigmap(module); + dict<SigBit, pool<int>> bits_to_nodes; + + if (config.ltr || config.alpha) + { + dict<Wire*, double> alpha_inputs, alpha_outputs; + + if (config.alpha) + { + dict<string, Wire*> alpha_order; + + for (auto wire : module->wires()) { + if (wire->port_input || wire->port_output) + alpha_order[wire->name.str()] = wire; + } + + alpha_order.sort(); + + for (auto &it : alpha_order) { + if (it.second->port_input) { + int idx = GetSize(alpha_inputs); + alpha_inputs[it.second] = idx + 0.5; + } + if (it.second->port_output) { + int idx = GetSize(alpha_outputs); + alpha_outputs[it.second] = idx + 0.5; + } + } + } + + for (auto wire : module->wires()) + { + if (!wire->port_input && !wire->port_output) + continue; + + int idx = GetSize(nodes); + nodes.push_back(Node()); + + if (config.ltr) { + if (wire->port_input) + nodes[idx].tie(0.0); + else + nodes[idx].tie(1.0); + } + + if (config.alpha) { + if (wire->port_input) + nodes[idx].alt_tie(alpha_inputs.at(wire) / GetSize(alpha_inputs)); + else + nodes[idx].alt_tie(alpha_outputs.at(wire) / GetSize(alpha_outputs)); + } + + for (auto bit : sigmap(wire)) + bits_to_nodes[bit].insert(idx); + } + } + + for (auto cell : module->selected_cells()) + { + log_assert(cell_to_node.count(cell) == 0); + int idx = GetSize(nodes); + nodes.push_back(Node()); + + cell_to_node[cell] = GetSize(nodes); + nodes[idx].cell = cell; + + for (auto &conn : cell->connections()) + for (auto bit : sigmap(conn.second)) + bits_to_nodes[bit].insert(idx); + } + + for (auto &it : bits_to_nodes) + { + if (GetSize(it.second) > 100) + continue; + + for (int idx1 : it.second) + for (int idx2 : it.second) + if (idx1 < idx2) + edges[pair<int, int>(idx1, idx2)] += 1.0 / GetSize(it.second); + } + } + + void solve(bool alt_mode = false) + { + // A := constraint_matrix + // y := constraint_rhs_vector + // + // AA = A' * A + // Ay = A' * y + // + // M := [AA Ay] + + if (config.verbose) + log("> System size: %d^2\n", GetSize(nodes)); + + // Row major order + int N = GetSize(nodes), N1 = N+1; + vector<double> M(N * N1); + + if (config.verbose) + log("> Edge constraints: %d\n", GetSize(edges)); + + // Edge constraints: + // A[i,:] := [ 0 0 .... 0 weight 0 ... 0 -weight 0 ... 0 0], y[i] := 0 + // + // i.e. nonzero columns in A[i,:] at the two node indices. + for (auto &edge : edges) + { + int idx1 = edge.first.first; + int idx2 = edge.first.second; + double weight = edge.second * (1.0 + xorshift32() * 1e-3); + + M[idx1 + idx1*N1] += weight * weight; + M[idx2 + idx2*N1] += weight * weight; + + M[idx1 + idx2*N1] += -weight * weight; + M[idx2 + idx1*N1] += -weight * weight; + } + + if (config.verbose) + log("> Node constraints: %d\n", GetSize(nodes)); + + // Node constraints: + // A[i,:] := [ 0 0 .... 0 weight 0 ... 0 0], y[i] := weight * pos + // + // i.e. nonzero column in A[i,:] at the node index + // + // "tied" nodes have a large weight, pinning them in position. Untied + // nodes have a small weight, giving then a tiny preference to stay at + // the current position, making sure that AA never is singular. + for (int idx = 0; idx < GetSize(nodes); idx++) + { + auto &node = nodes[idx]; + double rhs = (alt_mode ? node.alt_pos : node.pos); + + double weight = 1e-3; + if (alt_mode ? node.alt_tied : node.tied) + weight = 1e3; + weight *= (1.0 + xorshift32() * 1e-3); + + M[idx + idx*N1] += weight * weight; + M[N + idx*N1] += rhs * weight * weight; + } + +#ifdef LOG_MATRICES + log("\n"); + for (int i = 0; i < N; i++) { + for (int j = 0; j < N+1; j++) + log(" %10.2e", M[(N+1)*i + j]); + log("\n"); + } +#endif + + if (config.verbose) + log("> Solving\n"); + + // Solve "AA*x = Ay" + // (least squares fit for "A*x = y") + // + // Using gaussian elimination to get M := [Id x] + + vector<int> pivot_cache; + vector<int> queue; + + for (int i = 0; i < N; i++) + queue.push_back(i); + + // gaussian elimination + for (int i = 0; i < N; i++) + { + if (config.verbose && ((i+1) % (N/15)) == 0) + log("> Solved %d%%: %d/%d\n", (100*(i+1))/N, i+1, N); + + // find best row + int best_row = queue.front(); + int best_row_queue_idx = 0; + double best_row_absval = 0; + + for (int k = 0; k < GetSize(queue); k++) { + int row = queue[k]; + double absval = fabs(M[i + row*N1]); + if (absval > best_row_absval) { + best_row = row; + best_row_queue_idx = k; + best_row_absval = absval; + } + } + + int row = best_row; + pivot_cache.push_back(row); + + queue[best_row_queue_idx] = queue.back(); + queue.pop_back(); + + // normalize row + for (int k = i+1; k < N1; k++) + M[k + row*N1] /= M[i + row*N1]; + M[i + row*N1] = 1.0; + + // elimination + for (int other_row : queue) { + double d = M[i + other_row*N1]; + for (int k = i+1; k < N1; k++) + M[k + other_row*N1] -= d*M[k + row*N1]; + M[i + other_row*N1] = 0.0; + } + } + + if (config.verbose) + log("> Solved\n"); + + log_assert(queue.empty()); + log_assert(GetSize(pivot_cache) == N); + + // back substitution + for (int i = N-1; i >= 0; i--) + for (int j = i+1; j < N; j++) + { + int row = pivot_cache[i]; + int other_row = pivot_cache[j]; + M[N + row*N1] -= M[j + row*N1] * M[N + other_row*N1]; + M[j + row*N1] = 0.0; + } + +#ifdef LOG_MATRICES + log("\n"); + for (int i = 0; i < N; i++) { + for (int j = 0; j < N+1; j++) + log(" %10.2e", M[(N+1)*i + j]); + log("\n"); + } +#endif + + if (config.verbose) + log("> Update nodes\n"); + + // update node positions + for (int i = 0; i < N; i++) + { + double v = M[(N+1)*i + N]; + double c = alt_mode ? alt_midpos : midpos; + double r = alt_mode ? alt_radius : radius; + + if (std::isfinite(v)) { + v = min(v, c+r); + v = max(v, c-r); + } else { + v = c; + } + + if (alt_mode) { + if (!nodes[i].alt_tied) + nodes[i].alt_pos = v; + } else { + if (!nodes[i].tied) + nodes[i].pos = v; + } + } + } + + void log_cell_coordinates(int indent, bool log_all_nodes = false) + { + for (auto &node : nodes) + { + if (node.cell == nullptr && !log_all_nodes) + continue; + + for (int i = 0; i < indent; i++) + log(" "); + + if (direction == 'x') + log("X=%.2f, Y=%.2f", node.pos, node.alt_pos); + else + log("X=%.2f, Y=%.2f", node.alt_pos, node.pos); + + if (node.tied) + log(" [%c-tied]", direction); + + if (node.alt_tied) + log(" [%c-tied]", direction == 'x' ? 'y' : 'x'); + + if (node.cell != nullptr) + log(" %s (%s)", log_id(node.cell), log_id(node.cell->type)); + else + log(" (none)"); + + log("\n"); + } + } + + void dump_svg(const pool<int> *green_nodes = nullptr, double median = -1) + { + double x_center = direction == 'x' ? midpos : alt_midpos; + double y_center = direction == 'y' ? midpos : alt_midpos; + + double x_radius = direction == 'x' ? radius : alt_radius; + double y_radius = direction == 'y' ? radius : alt_radius; + + config.dump_file << stringf("<svg height=\"240\" width=\"470\">\n"); + config.dump_file << stringf("<rect x=\"0\" y=\"0\" width=\"470\" height=\"240\" style=\"fill:rgb(250,250,200);\" />\n"); + config.dump_file << stringf("<rect x=\"20\" y=\"20\" width=\"200\" height=\"200\" style=\"fill:rgb(200,200,200);\" />\n"); + config.dump_file << stringf("<rect x=\"250\" y=\"20\" width=\"200\" height=\"200\" style=\"fill:rgb(200,200,200);\" />\n"); + + double win_x = 250 + 200 * (direction == 'x' ? midpos - radius : alt_midpos - alt_radius); + double win_y = 20 + 200 * (direction == 'y' ? midpos - radius : alt_midpos - alt_radius); + + double win_w = 200 * (direction == 'x' ? 2*radius : 2*alt_radius); + double win_h = 200 * (direction == 'y' ? 2*radius : 2*alt_radius); + + config.dump_file << stringf("<rect x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%.2f\" " + "style=\"stroke:rgb(0,0,0);stroke-width:1;fill:none\" />\n", win_x, win_y, win_w, win_h); + + if (median >= 0) + { + double x1 = 20.0, x2 = 220.0, y1 = 20.0, y2 = 220.0; + + if (direction == 'x') + x1 = x2 = 120 + 100 * (median - x_center) / x_radius; + else + y1 = y2 = 120 + 100 * (median - y_center) / y_radius; + + config.dump_file << stringf("<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\" " + "style=\"stroke:rgb(150,0,150);stroke-width:1\" />\n", x1, y1, x2, y2); + } + + for (auto &edge : edges) + { + auto &node1 = nodes[edge.first.first]; + auto &node2 = nodes[edge.first.second]; + + double x1 = direction == 'x' ? node1.pos : node1.alt_pos; + double y1 = direction == 'y' ? node1.pos : node1.alt_pos; + + double x2 = direction == 'x' ? node2.pos : node2.alt_pos; + double y2 = direction == 'y' ? node2.pos : node2.alt_pos; + + x1 = 120 + 100 * (x1 - x_center) / x_radius; + y1 = 120 + 100 * (y1 - y_center) / y_radius; + + x2 = 120 + 100 * (x2 - x_center) / x_radius; + y2 = 120 + 100 * (y2 - y_center) / y_radius; + + config.dump_file << stringf("<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\" " + "style=\"stroke:rgb(0,0,0);stroke-width:1\" />\n", x1, y1, x2, y2); + } + + for (int i = 0; i < GetSize(nodes); i++) + { + auto &node = nodes[i]; + + double x = direction == 'x' ? node.pos : node.alt_pos; + double y = direction == 'y' ? node.pos : node.alt_pos; + + x = 120 + 100 * (x - x_center) / x_radius; + y = 120 + 100 * (y - y_center) / y_radius; + + const char *color = node.cell == nullptr ? "blue" : "red"; + + if (green_nodes != nullptr && green_nodes->count(i)) + color = "green"; + + config.dump_file << stringf("<circle cx=\"%.2f\" cy=\"%.2f\" r=\"3\" fill=\"%s\"/>\n", x, y, color); + } + + config.dump_file << stringf("</svg>\n"); + } + + void run_worker(int indent) + { + int count_cells = 0; + + for (auto &node : nodes) + if (node.cell != nullptr) + count_cells++; + + for (int i = 0; i < indent; i++) + log(" "); + + string range_str; + + if (direction == 'x') + range_str = stringf("X=%.2f:%.2f, Y=%.2f:%.2f", + midpos - radius, midpos + radius, + alt_midpos - alt_radius, alt_midpos + alt_radius); + else + range_str = stringf("X=%.2f:%.2f, Y=%.2f:%.2f", + alt_midpos - alt_radius, alt_midpos + alt_radius, + midpos - radius, midpos + radius); + + log("%c-qwp on %s with %d cells, %d nodes, and %d edges.\n", direction, + range_str.c_str(), count_cells, GetSize(nodes), GetSize(edges)); + + solve(); + solve(true); + + // detect median position and check for break condition + + vector<pair<double, int>> sorted_pos; + for (int i = 0; i < GetSize(nodes); i++) + if (nodes[i].cell != nullptr) + sorted_pos.push_back(pair<double, int>(nodes[i].pos, i)); + + std::sort(sorted_pos.begin(), sorted_pos.end()); + int median_sidx = GetSize(sorted_pos)/2; + double median = sorted_pos[median_sidx].first; + + double left_scale = radius / (median - (midpos - radius)); + double right_scale = radius / ((midpos + radius) - median); + + if (config.dump_file.is_open()) + { + config.dump_file << stringf("<h4>LSQ %c-Solution for %s:</h4>\n", direction, range_str.c_str()); + + pool<int> green_nodes; + for (int i = 0; i < median_sidx; i++) + green_nodes.insert(sorted_pos[i].second); + + dump_svg(&green_nodes, median); + } + + for (auto &node : nodes) + { + double rel_pos = node.pos - median; + if (rel_pos < 0) { + node.pos = midpos + left_scale*rel_pos; + if (std::isfinite(node.pos)) { + 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 = max(node.pos, midpos); + node.pos = min(node.pos, midpos + radius); + } else + node.pos = midpos + radius/2; + } + } + + if (GetSize(sorted_pos) < 2 || (2*radius <= config.grid && 2*alt_radius <= config.grid)) { + log_cell_coordinates(indent + 1); + return; + } + + // create child workers + + char child_direction = direction == 'x' ? 'y' : 'x'; + + QwpWorker left_worker(config, module, child_direction); + QwpWorker right_worker(config, module, child_direction); + + // duplicate nodes into child workers + + dict<int, int> left_nodes, right_nodes; + + for (int k = 0; k < GetSize(sorted_pos); k++) + { + int i = sorted_pos[k].second; + + if (k < median_sidx) { + left_nodes[i] = GetSize(left_worker.nodes); + left_worker.nodes.push_back(nodes[i]); + if (left_worker.nodes.back().pos > midpos) + left_worker.nodes.back().pos = midpos; + left_worker.nodes.back().swap_alt(); + } else { + right_nodes[i] = GetSize(right_worker.nodes); + right_worker.nodes.push_back(nodes[i]); + if (right_worker.nodes.back().pos < midpos) + right_worker.nodes.back().pos = midpos; + right_worker.nodes.back().swap_alt(); + } + } + + // duplicate edges into child workers, project nodes as needed + + for (auto &edge : edges) + { + int idx1 = edge.first.first; + int idx2 = edge.first.second; + double weight = edge.second; + + if (nodes[idx1].cell == nullptr && nodes[idx2].cell == nullptr) + continue; + + int left_idx1 = left_nodes.count(idx1) ? left_nodes.at(idx1) : -1; + int left_idx2 = left_nodes.count(idx2) ? left_nodes.at(idx2) : -1; + + int right_idx1 = right_nodes.count(idx1) ? right_nodes.at(idx1) : -1; + int right_idx2 = right_nodes.count(idx2) ? right_nodes.at(idx2) : -1; + + if (left_idx1 >= 0 && left_worker.nodes[left_idx1].cell && left_idx2 < 0) { + left_idx2 = left_nodes[idx2] = GetSize(left_worker.nodes); + left_worker.nodes.push_back(nodes[idx2]); + left_worker.nodes.back().proj_left(midpos); + left_worker.nodes.back().swap_alt(); + } else + if (left_idx2 >= 0 && left_worker.nodes[left_idx2].cell && left_idx1 < 0) { + left_idx1 = left_nodes[idx1] = GetSize(left_worker.nodes); + left_worker.nodes.push_back(nodes[idx1]); + left_worker.nodes.back().proj_left(midpos); + left_worker.nodes.back().swap_alt(); + } + + if (right_idx1 >= 0 && right_worker.nodes[right_idx1].cell && right_idx2 < 0) { + right_idx2 = right_nodes[idx2] = GetSize(right_worker.nodes); + right_worker.nodes.push_back(nodes[idx2]); + right_worker.nodes.back().proj_right(midpos); + right_worker.nodes.back().swap_alt(); + } else + if (right_idx2 >= 0 && right_worker.nodes[right_idx2].cell && right_idx1 < 0) { + right_idx1 = right_nodes[idx1] = GetSize(right_worker.nodes); + right_worker.nodes.push_back(nodes[idx1]); + right_worker.nodes.back().proj_right(midpos); + right_worker.nodes.back().swap_alt(); + } + + if (left_idx1 >= 0 && left_idx2 >= 0) + left_worker.edges[pair<int, int>(left_idx1, left_idx2)] += weight; + + if (right_idx1 >= 0 && right_idx2 >= 0) + right_worker.edges[pair<int, int>(right_idx1, right_idx2)] += weight; + } + + // run child workers + + left_worker.midpos = right_worker.midpos = alt_midpos; + left_worker.radius = right_worker.radius = alt_radius; + + left_worker.alt_midpos = midpos - radius/2; + right_worker.alt_midpos = midpos + radius/2; + left_worker.alt_radius = right_worker.alt_radius = radius/2; + + left_worker.run_worker(indent+1); + right_worker.run_worker(indent+1); + + // re-integrate results + + for (auto &it : left_nodes) + if (left_worker.nodes[it.second].cell != nullptr) { + nodes[it.first].pos = left_worker.nodes[it.second].alt_pos; + nodes[it.first].alt_pos = left_worker.nodes[it.second].pos; + } + + for (auto &it : right_nodes) + if (right_worker.nodes[it.second].cell != nullptr) { + nodes[it.first].pos = right_worker.nodes[it.second].alt_pos; + nodes[it.first].alt_pos = right_worker.nodes[it.second].pos; + } + + if (config.dump_file.is_open()) { + config.dump_file << stringf("<h4>Final %c-Solution for %s:</h4>\n", direction, range_str.c_str()); + dump_svg(); + } + } + + void histogram(const vector<double> &values) + { + if (values.empty()) { + log("no data\n"); + return; + } + + double min_value = values.front(); + double max_value = values.front(); + + for (auto &v : values) { + min_value = min(min_value, v); + max_value = max(max_value, v); + } + + if (fabs(max_value - min_value) < 0.001) { + log("all values in range %f .. %f\n", min_value, max_value); + return; + } + + vector<int> buckets(60); + int max_bucket_val = 0; + + for (auto &v : values) { + 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--) { + for (int k = 0; k < GetSize(buckets); k++) { + int v = 10 * buckets[k] / max_bucket_val; + if (v >= 2*i+1) + log(v == 2*i+1 ? "." : ":"); + else + log(i == 0 ? (buckets[k] > 0 ? "," : "_") : " "); + } + log("\n"); + } + log("%-30f%30f\n", min_value, max_value); + } + + void run() + { + log("\n"); + log("Running qwp on module %s..\n", log_id(module)); + + if (config.dump_file.is_open()) + config.dump_file << stringf("<h3>QWP protocol for module %s:</h3>\n", log_id(module)); + + load_module(); + + midpos = 0.5; + radius = 0.5; + alt_midpos = 0.5; + alt_radius = 0.5; + run_worker(1); + + for (auto &node : nodes) + if (node.cell != nullptr) + node.cell->attributes["\\qwp_position"] = stringf("%f %f", node.pos, node.alt_pos); + + vector<double> edge_lengths; + vector<double> weighted_edge_lengths; + + double total_edge_length = 0; + double total_weighted_edge_length = 0; + + for (auto &edge : edges) + { + auto &node1 = nodes[edge.first.first]; + auto &node2 = nodes[edge.first.second]; + + double distance = sqrt(pow(node1.pos - node2.pos, 2) + pow(node1.alt_pos - node2.alt_pos, 2)); + double weighted_distance = distance * edge.second; + + edge_lengths.push_back(distance); + weighted_edge_lengths.push_back(weighted_distance); + + total_edge_length += distance; + total_weighted_edge_length += weighted_distance; + } + + log("\n"); + log("Summary for module %s:\n", log_id(module)); + log("Number of edges: %d\n", GetSize(edges)); + log("Total edge length: %f\n", total_edge_length); + log("Total weighted edge length: %f\n", total_weighted_edge_length); + + log("\n"); + log("Histogram over edge lengths:\n"); + histogram(edge_lengths); + + log("\n"); + log("Histogram over weighted edge lengths:\n"); + histogram(weighted_edge_lengths); + } +}; + +struct QwpPass : public Pass { + QwpPass() : Pass("qwp", "quadratic wirelength placer") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" qwp [options] [selection]\n"); + log("\n"); + log("This command runs quadratic wirelength placement on the selected modules and\n"); + log("annotates the cells in the design with 'qwp_position' attributes.\n"); + log("\n"); + log(" -ltr\n"); + log(" Add left-to-right constraints: constrain all inputs on the left border\n"); + log(" outputs to the right border.\n"); + log("\n"); + log(" -alpha\n"); + log(" Add constraints for inputs/outputs to be placed in alphanumerical\n"); + log(" order along the y-axis (top-to-bottom).\n"); + log("\n"); + log(" -grid N\n"); + log(" Number of grid divisions in x- and y-direction. (default=16)\n"); + log("\n"); + log(" -dump <html_file_name>\n"); + log(" Dump a protocol of the placement algorithm to the html file.\n"); + log("\n"); + log(" -v\n"); + log(" Verbose solver output for profiling or debugging\n"); + log("\n"); + log("Note: This implementation of a quadratic wirelength placer uses exact\n"); + log("dense matrix operations. It is only a toy-placer for small circuits.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + QwpConfig config; + xorshift32_state = 123456789; + + log_header(design, "Executing QWP pass (quadratic wirelength placer).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-ltr") { + config.ltr = true; + continue; + } + if (args[argidx] == "-alpha") { + config.alpha = true; + continue; + } + if (args[argidx] == "-v") { + config.verbose = true; + continue; + } + if (args[argidx] == "-grid" && argidx+1 < args.size()) { + config.grid = 1.0 / atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-dump" && argidx+1 < args.size()) { + config.dump_file.open(args[++argidx], std::ofstream::trunc); + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + QwpWorker worker(config, module); + worker.run(); + +#ifdef PYPLOT_EDGES + log("\n"); + log("plt.figure(figsize=(10, 10));\n"); + + for (auto &edge : worker.edges) { + log("plt.plot([%.2f, %.2f], [%.2f, %.2f], \"r-\");\n", + worker.nodes[edge.first.first].pos, + worker.nodes[edge.first.second].pos, + worker.nodes[edge.first.first].alt_pos, + worker.nodes[edge.first.second].alt_pos); + } + + for (auto &node : worker.nodes) { + const char *style = node.cell != nullptr ? "ko" : "ks"; + log("plt.plot([%.2f], [%.2f], \"%s\");\n", node.pos, node.alt_pos, style); + } +#endif + } + } +} QwpPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 17d803e96..6a002869b 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -2,11 +2,11 @@ * 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 @@ -76,12 +76,17 @@ struct RenamePass : public Pass { log("Assign private names (the ones with $-prefix) to all selected wires and cells\n"); log("with public names. This ignores all selected ports.\n"); log("\n"); + log(" rename -top new_name\n"); + log("\n"); + log("Rename top module.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { std::string pattern_prefix = "_", pattern_suffix = "_"; bool flag_enumerate = false; bool flag_hide = false; + bool flag_top = false; bool got_mode = false; size_t argidx; @@ -98,6 +103,11 @@ struct RenamePass : public Pass { got_mode = true; continue; } + if (arg == "-top" && !got_mode) { + flag_top = true; + got_mode = true; + continue; + } if (arg == "-pattern" && argidx+1 < args.size() && args[argidx+1].find('%') != std::string::npos) { int pos = args[++argidx].find('%'); pattern_prefix = args[argidx].substr(0, pos); @@ -171,6 +181,21 @@ struct RenamePass : public Pass { } } else + if (flag_top) + { + if (argidx+1 != args.size()) + log_cmd_error("Invalid number of arguments!\n"); + + IdString new_name = RTLIL::escape_id(args[argidx]); + RTLIL::Module *module = design->top_module(); + + if (module == NULL) + log_cmd_error("No top module found!\n"); + + log("Renaming module %s to %s.\n", log_id(module), log_id(new_name)); + design->rename(module, new_name); + } + else { if (argidx+2 != args.size()) log_cmd_error("Invalid number of arguments!\n"); @@ -203,5 +228,5 @@ struct RenamePass : public Pass { } } } RenamePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/scatter.cc b/passes/cmds/scatter.cc index 1cd55ecb0..f083e1f67 100644 --- a/passes/cmds/scatter.cc +++ b/passes/cmds/scatter.cc @@ -2,11 +2,11 @@ * 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 @@ -69,5 +69,5 @@ struct ScatterPass : public Pass { } } } ScatterPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/scc.cc b/passes/cmds/scc.cc index f7f50ab2a..bb6d74474 100644 --- a/passes/cmds/scc.cc +++ b/passes/cmds/scc.cc @@ -2,11 +2,11 @@ * 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 @@ -18,7 +18,7 @@ */ // [[CITE]] Tarjan's strongly connected components algorithm -// Tarjan, R. E. (1972), "Depth-first search and linear graph algorithms", SIAM Journal on Computing 1 (2): 146–160, doi:10.1137/0201010 +// Tarjan, R. E. (1972), "Depth-first search and linear graph algorithms", SIAM Journal on Computing 1 (2): 146-160, doi:10.1137/0201010 // http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm #include "kernel/register.h" @@ -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) @@ -100,7 +100,8 @@ struct SccWorker } } - SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool allCellTypes, int maxDepth) : design(design), module(module), sigmap(module) + SccWorker(RTLIL::Design *design, RTLIL::Module *module, bool nofeedbackMode, bool allCellTypes, int maxDepth) : + design(design), module(module), sigmap(module) { if (module->processes.size() > 0) { log("Skipping module %s as it contains processes (run 'proc' pass first).\n", module->name.c_str()); @@ -167,10 +168,22 @@ struct SccWorker labelCounter = 0; cellLabels.clear(); - while (workQueue.size() > 0) { + while (workQueue.size() > 0) + { RTLIL::Cell *cell = *workQueue.begin(); log_assert(cellStack.size() == 0); cellDepth.clear(); + + if (!nofeedbackMode && cellToNextCell[cell].count(cell)) { + log("Found an SCC:"); + std::set<RTLIL::Cell*> scc; + log(" %s", RTLIL::id2cstr(cell->name)); + cell2scc[cell] = sccList.size(); + scc.insert(cell); + sccList.push_back(scc); + log("\n"); + } + run(cell, 0, maxDepth); } @@ -212,10 +225,18 @@ struct SccPass : public Pass { log("This command identifies strongly connected components (aka logic loops) in the\n"); log("design.\n"); log("\n"); + log(" -expect <num>\n"); + log(" expect to find exactly <num> SSCs. A different number of SSCs will\n"); + log(" produce an error.\n"); + log("\n"); log(" -max_depth <num>\n"); - log(" limit to loops not longer than the specified number of cells. This can\n"); - log(" e.g. be useful in identifying local loops in a module that turns out\n"); - log(" to be one gigantic SCC.\n"); + log(" limit to loops not longer than the specified number of cells. This\n"); + log(" can e.g. be useful in identifying small local loops in a module that\n"); + log(" implements one large SCC.\n"); + log("\n"); + log(" -nofeedback\n"); + log(" do not count cells that have their output fed back into one of their\n"); + log(" inputs as single-cell scc.\n"); log("\n"); log(" -all_cell_types\n"); log(" Usually this command only considers internal non-memory cells. With\n"); @@ -239,9 +260,11 @@ struct SccPass : public Pass { std::map<std::string, std::string> setCellAttr, setWireAttr; bool allCellTypes = false; bool selectMode = false; + bool nofeedbackMode = false; int maxDepth = -1; + int expect = -1; - log_header("Executing SCC pass (detecting logic loops).\n"); + log_header(design, "Executing SCC pass (detecting logic loops).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -249,6 +272,14 @@ struct SccPass : public Pass { maxDepth = atoi(args[++argidx].c_str()); continue; } + if (args[argidx] == "-expect" && argidx+1 < args.size()) { + expect = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-nofeedback") { + nofeedbackMode = true; + continue; + } if (args[argidx] == "-all_cell_types") { allCellTypes = true; continue; @@ -282,16 +313,26 @@ struct SccPass : public Pass { log_cmd_error("The -set*_attr options are not implemented at the moment!\n"); RTLIL::Selection newSelection(false); + int scc_counter = 0; for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) { - SccWorker worker(design, mod_it.second, allCellTypes, maxDepth); + SccWorker worker(design, mod_it.second, nofeedbackMode, allCellTypes, maxDepth); + scc_counter += GetSize(worker.sccList); if (selectMode) worker.select(newSelection); } + if (expect >= 0) { + if (scc_counter == expect) + log("Found and expected %d SCCs.\n", scc_counter); + else + log_error("Found %d SCCs but expected %d.\n", scc_counter, expect); + } else + log("Found %d SCCs.\n", scc_counter); + if (selectMode) { log_assert(origSelectPos >= 0); design->selection_stack[origSelectPos] = newSelection; @@ -299,5 +340,5 @@ struct SccPass : public Pass { } } } SccPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc index 247765f0d..d2e1a2e2b 100644 --- a/passes/cmds/select.cc +++ b/passes/cmds/select.cc @@ -2,11 +2,11 @@ * 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 @@ -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_) @@ -196,6 +237,27 @@ static void select_op_submod(RTLIL::Design *design, RTLIL::Selection &lhs) } } +static void select_op_cells_to_modules(RTLIL::Design *design, RTLIL::Selection &lhs) +{ + RTLIL::Selection new_sel(false); + for (auto &mod_it : design->modules_) + if (lhs.selected_module(mod_it.first)) + for (auto &cell_it : mod_it.second->cells_) + if (lhs.selected_member(mod_it.first, cell_it.first) && design->modules_.count(cell_it.second->type)) + new_sel.selected_modules.insert(cell_it.second->type); + lhs = new_sel; +} + +static void select_op_module_to_cells(RTLIL::Design *design, RTLIL::Selection &lhs) +{ + RTLIL::Selection new_sel(false); + for (auto &mod_it : design->modules_) + for (auto &cell_it : mod_it.second->cells_) + if (design->modules_.count(cell_it.second->type) && lhs.selected_whole_module(cell_it.second->type)) + new_sel.selected_members[mod_it.first].insert(cell_it.first); + lhs = new_sel; +} + static void select_op_fullmod(RTLIL::Design *design, RTLIL::Selection &lhs) { lhs.optimize(design); @@ -365,7 +427,7 @@ static int parse_comma_list(std::set<RTLIL::IdString> &tokens, std::string str, } } -static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, CellTypes &ct) +static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::vector<expand_rule_t> &rules, std::set<RTLIL::IdString> &limits, int max_objects, char mode, CellTypes &ct, bool eval_only) { int sel_objects = 0; bool is_input, is_output; @@ -401,6 +463,8 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v for (auto &conn : cell.second->connections()) { char last_mode = '-'; + if (eval_only && !yosys_celltypes.cell_evaluable(cell.second->type)) + goto exclude_match; for (auto &rule : rules) { last_mode = rule.mode; if (rule.cell_types.size() > 0 && rule.cell_types.count(cell.second->type) == 0) @@ -433,9 +497,10 @@ static int select_op_expand(RTLIL::Design *design, RTLIL::Selection &lhs, std::v return sel_objects; } -static void select_op_expand(RTLIL::Design *design, std::string arg, char mode) +static void select_op_expand(RTLIL::Design *design, std::string arg, char mode, bool eval_only) { - int pos = mode == 'x' ? 2 : 3, levels = 1, rem_objects = -1; + int pos = (mode == 'x' ? 2 : 3) + (eval_only ? 1 : 0); + int levels = 1, rem_objects = -1; std::vector<expand_rule_t> rules; std::set<RTLIL::IdString> limits; @@ -526,7 +591,7 @@ static void select_op_expand(RTLIL::Design *design, std::string arg, char mode) #endif while (levels-- > 0 && rem_objects != 0) { - int num_objects = select_op_expand(design, work_stack.back(), rules, limits, rem_objects, mode, ct); + int num_objects = select_op_expand(design, work_stack.back(), rules, limits, rem_objects, mode, ct, eval_only); if (num_objects == 0) break; rem_objects -= num_objects; @@ -540,7 +605,7 @@ static void select_filter_active_mod(RTLIL::Design *design, RTLIL::Selection &se { if (design->selected_active_module.empty()) return; - + if (sel.full_selection) { sel.full_selection = false; sel.selected_modules.clear(); @@ -610,11 +675,27 @@ 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"); select_op_submod(design, work_stack[work_stack.size()-1]); } else + if (arg == "%M") { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%M.\n"); + select_op_cells_to_modules(design, work_stack[work_stack.size()-1]); + } else + if (arg == "%C") { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%M.\n"); + select_op_module_to_cells(design, work_stack[work_stack.size()-1]); + } else if (arg == "%c") { if (work_stack.size() < 1) log_cmd_error("Must have at least one element on the stack for operator %%c.\n"); @@ -633,17 +714,32 @@ static void select_stmt(RTLIL::Design *design, std::string arg) if (arg == "%x" || (arg.size() > 2 && arg.substr(0, 2) == "%x" && (arg[2] == ':' || arg[2] == '*' || arg[2] == '.' || ('0' <= arg[2] && arg[2] <= '9')))) { if (work_stack.size() < 1) log_cmd_error("Must have at least one element on the stack for operator %%x.\n"); - select_op_expand(design, arg, 'x'); + select_op_expand(design, arg, 'x', false); } else if (arg == "%ci" || (arg.size() > 3 && arg.substr(0, 3) == "%ci" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) { if (work_stack.size() < 1) log_cmd_error("Must have at least one element on the stack for operator %%ci.\n"); - select_op_expand(design, arg, 'i'); + select_op_expand(design, arg, 'i', false); } else if (arg == "%co" || (arg.size() > 3 && arg.substr(0, 3) == "%co" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) { if (work_stack.size() < 1) log_cmd_error("Must have at least one element on the stack for operator %%co.\n"); - select_op_expand(design, arg, 'o'); + select_op_expand(design, arg, 'o', false); + } else + if (arg == "%xe" || (arg.size() > 3 && arg.substr(0, 3) == "%xe" && (arg[3] == ':' || arg[3] == '*' || arg[3] == '.' || ('0' <= arg[3] && arg[3] <= '9')))) { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%xe.\n"); + select_op_expand(design, arg, 'x', true); + } else + if (arg == "%cie" || (arg.size() > 4 && arg.substr(0, 4) == "%cie" && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%cie.\n"); + select_op_expand(design, arg, 'i', true); + } else + if (arg == "%coe" || (arg.size() > 4 && arg.substr(0, 4) == "%coe" && (arg[4] == ':' || arg[4] == '*' || arg[4] == '.' || ('0' <= arg[4] && arg[4] <= '9')))) { + if (work_stack.size() < 1) + log_cmd_error("Must have at least one element on the stack for operator %%coe.\n"); + select_op_expand(design, arg, 'o', true); } else log_cmd_error("Unknown selection operator '%s'.\n", arg.c_str()); if (work_stack.size() >= 1) @@ -684,7 +780,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg) select_filter_active_mod(design, work_stack.back()); return; } - + sel.full_selection = false; for (auto &mod_it : design->modules_) { @@ -856,7 +952,7 @@ struct SelectPass : public Pass { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" select [ -add | -del | -set <name> ] {-read <filename> | <selection>}\n"); - log(" select [ -assert-none | -assert-any ] {-read <filename> | <selection>}\n"); + log(" select [ <assert_option> ] {-read <filename> | <selection>}\n"); log(" select [ -list | -write <filename> | -count | -clear ]\n"); log(" select -module <modname>\n"); log("\n"); @@ -892,6 +988,14 @@ struct SelectPass : public Pass { log(" do not modify the current selection. instead assert that the given\n"); log(" selection contains exactly N objects.\n"); log("\n"); + log(" -assert-max N\n"); + log(" do not modify the current selection. instead assert that the given\n"); + log(" selection contains less than or exactly N objects.\n"); + log("\n"); + log(" -assert-min N\n"); + log(" do not modify the current selection. instead assert that the given\n"); + log(" selection contains at least N objects.\n"); + log("\n"); log(" -list\n"); log(" list all objects in the current selection\n"); log("\n"); @@ -1012,7 +1116,7 @@ struct SelectPass : public Pass { log(" like %%d but swap the roles of two top sets on the stack\n"); log("\n"); log(" %%c\n"); - log(" create a copy of the top set rom the stack and push it\n"); + log(" create a copy of the top set from the stack and push it\n"); log("\n"); log(" %%x[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n"); log(" expand top set <num1> num times according to the specified rules.\n"); @@ -1029,19 +1133,31 @@ struct SelectPass : public Pass { log("\n"); log(" %%ci[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n"); log(" %%co[<num1>|*][.<num2>][:<rule>[:<rule>..]]\n"); - log(" simmilar to %%x, but only select input (%%ci) or output cones (%%co)\n"); + log(" similar to %%x, but only select input (%%ci) or output cones (%%co)\n"); + log("\n"); + log(" %%xe[...] %%cie[...] %%coe\n"); + log(" like %%x, %%ci, and %%co but only consider combinatorial cells\n"); log("\n"); log(" %%a\n"); log(" expand top set by selecting all wires that are (at least in part)\n"); log(" aliases for selected wires.\n"); log("\n"); log(" %%s\n"); - log(" expand top set by adding all modules of instantiated cells in selected\n"); + log(" expand top set by adding all modules that implement cells in selected\n"); log(" modules\n"); log("\n"); log(" %%m\n"); log(" expand top set by selecting all modules that contain selected objects\n"); log("\n"); + log(" %%M\n"); + log(" select modules that implement selected cells\n"); + log("\n"); + 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"); @@ -1060,6 +1176,8 @@ struct SelectPass : public Pass { bool assert_none = false; bool assert_any = false; int assert_count = -1; + int assert_max = -1; + int assert_min = -1; std::string write_file, read_file; std::string set_name, sel_str; @@ -1089,6 +1207,14 @@ struct SelectPass : public Pass { assert_count = atoi(args[++argidx].c_str()); continue; } + if (arg == "-assert-max" && argidx+1 < args.size()) { + assert_max = atoi(args[++argidx].c_str()); + continue; + } + if (arg == "-assert-min" && argidx+1 < args.size()) { + assert_min = atoi(args[++argidx].c_str()); + continue; + } if (arg == "-clear") { clear_mode = true; continue; @@ -1165,14 +1291,14 @@ struct SelectPass : public Pass { if (none_mode && args.size() != 2) log_cmd_error("Option -none can not be combined with any other options.\n"); - if (add_mode + del_mode + assert_none + assert_any + (assert_count >= 0) > 1) - log_cmd_error("Options -add, -del, -assert-none, -assert-any or -assert-count can not be combined.\n"); + if (add_mode + del_mode + assert_none + assert_any + (assert_count >= 0) + (assert_max >= 0) + (assert_min >= 0) > 1) + log_cmd_error("Options -add, -del, -assert-none, -assert-any, assert-count, -assert-max or -assert-min can not be combined.\n"); - if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any || assert_count >= 0)) - log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none, -assert-any or -assert-count.\n"); + if ((list_mode || !write_file.empty() || count_mode) && (add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0)) + log_cmd_error("Options -list, -write and -count can not be combined with -add, -del, -assert-none, -assert-any, assert-count, -assert-max, or -assert-min.\n"); - if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || del_mode || assert_none || assert_any || assert_count >= 0)) - log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -assert-none, -assert-any or -assert-count.\n"); + if (!set_name.empty() && (list_mode || !write_file.empty() || count_mode || add_mode || del_mode || assert_none || assert_any || assert_count >= 0 || assert_max >= 0 || assert_min >= 0)) + log_cmd_error("Option -set can not be combined with -list, -write, -count, -add, -del, -assert-none, -assert-any, -assert-count, -assert-max, or -assert-min.\n"); if (work_stack.size() == 0 && got_module) { RTLIL::Selection sel; @@ -1261,8 +1387,9 @@ struct SelectPass : public Pass { { if (work_stack.size() == 0) log_cmd_error("No selection to check.\n"); + work_stack.back().optimize(design); if (!work_stack.back().empty()) - log_error("Assertation failed: selection is not empty:%s\n", sel_str.c_str()); + log_error("Assertion failed: selection is not empty:%s\n", sel_str.c_str()); return; } @@ -1270,12 +1397,13 @@ struct SelectPass : public Pass { { if (work_stack.size() == 0) log_cmd_error("No selection to check.\n"); + work_stack.back().optimize(design); if (work_stack.back().empty()) - log_error("Assertation failed: selection is empty:%s\n", sel_str.c_str()); + log_error("Assertion failed: selection is empty:%s\n", sel_str.c_str()); return; } - if (assert_count >= 0) + if (assert_count >= 0 || assert_max >= 0 || assert_min >= 0) { int total_count = 0; if (work_stack.size() == 0) @@ -1297,9 +1425,15 @@ struct SelectPass : public Pass { if (sel->selected_member(mod_it.first, it.first)) total_count++; } - if (assert_count != total_count) - log_error("Assertation failed: selection contains %d elements instead of the asserted %d:%s\n", + if (assert_count >= 0 && assert_count != total_count) + log_error("Assertion failed: selection contains %d elements instead of the asserted %d:%s\n", total_count, assert_count, sel_str.c_str()); + if (assert_max >= 0 && assert_max < total_count) + log_error("Assertion failed: selection contains %d elements, more than the maximum number %d:%s\n", + total_count, assert_max, sel_str.c_str()); + if (assert_min >= 0 && assert_min > total_count) + log_error("Assertion failed: selection contains %d elements, less than the minimum number %d:%s\n", + total_count, assert_min, sel_str.c_str()); return; } @@ -1328,7 +1462,7 @@ struct SelectPass : public Pass { design->selection_stack.back().optimize(design); } } SelectPass; - + struct CdPass : public Pass { CdPass() : Pass("cd", "a shortcut for 'select -module <name>'") { } virtual void help() @@ -1343,7 +1477,7 @@ struct CdPass : public Pass { log(" cd <cellname>\n"); log("\n"); log("When no module with the specified name is found, but there is a cell\n"); - log("with the specified name in the current module, then this is equivialent\n"); + log("with the specified name in the current module, then this is equivalent\n"); log("to 'cd <celltype>'.\n"); log("\n"); log(" cd ..\n"); @@ -1400,7 +1534,7 @@ static void log_matches(const char *title, Module *module, T list) log(" %s\n", RTLIL::id2cstr(id)); } } - + struct LsPass : public Pass { LsPass() : Pass("ls", "list modules or objects in modules") { } virtual void help() @@ -1444,5 +1578,5 @@ struct LsPass : public Pass { } } } LsPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/setattr.cc b/passes/cmds/setattr.cc index 9a6d8a038..9b05ae32f 100644 --- a/passes/cmds/setattr.cc +++ b/passes/cmds/setattr.cc @@ -2,11 +2,11 @@ * 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 @@ -32,25 +32,20 @@ struct setunset_t setunset_t(std::string unset_name) : name(RTLIL::escape_id(unset_name)), value(), unset(true) { } - setunset_t(std::string set_name, std::vector<std::string> args, size_t &argidx) : name(RTLIL::escape_id(set_name)), value(), unset(false) + setunset_t(std::string set_name, std::string set_value) : name(RTLIL::escape_id(set_name)), value(), unset(false) { - if (!args[argidx].empty() && args[argidx][0] == '"') { - std::string str = args[argidx++].substr(1); - while (str.size() != 0 && str[str.size()-1] != '"' && argidx < args.size()) - str += args[argidx++]; - if (str.size() != 0 && str[str.size()-1] == '"') - str = str.substr(0, str.size()-1); - value = RTLIL::Const(str); + if (set_value.substr(0, 1) == "\"" && set_value.substr(GetSize(set_value)-1) == "\"") { + value = RTLIL::Const(set_value.substr(1, GetSize(set_value)-2)); } else { RTLIL::SigSpec sig_value; - if (!RTLIL::SigSpec::parse(sig_value, NULL, args[argidx++])) - log_cmd_error("Can't decode value '%s'!\n", args[argidx-1].c_str()); + if (!RTLIL::SigSpec::parse(sig_value, NULL, set_value)) + log_cmd_error("Can't decode value '%s'!\n", set_value.c_str()); value = sig_value.as_const(); } } }; -static void do_setunset(dict<RTLIL::IdString, RTLIL::Const> &attrs, std::vector<setunset_t> &list) +static void do_setunset(dict<RTLIL::IdString, RTLIL::Const> &attrs, const std::vector<setunset_t> &list) { for (auto &item : list) if (item.unset) @@ -84,9 +79,9 @@ struct SetattrPass : public Pass { { std::string arg = args[argidx]; if (arg == "-set" && argidx+2 < args.size()) { - argidx += 2; - setunset_list.push_back(setunset_t(args[argidx-1], args, argidx)); - argidx--; + string set_key = args[++argidx]; + string set_val = args[++argidx]; + setunset_list.push_back(setunset_t(set_key, set_val)); continue; } if (arg == "-unset" && argidx+1 < args.size()) { @@ -132,7 +127,7 @@ struct SetattrPass : public Pass { } } } SetattrPass; - + struct SetparamPass : public Pass { SetparamPass() : Pass("setparam", "set/unset parameters on objects") { } virtual void help() @@ -154,9 +149,9 @@ struct SetparamPass : public Pass { { std::string arg = args[argidx]; if (arg == "-set" && argidx+2 < args.size()) { - argidx += 2; - setunset_list.push_back(setunset_t(args[argidx-1], args, argidx)); - argidx--; + string set_key = args[++argidx]; + string set_val = args[++argidx]; + setunset_list.push_back(setunset_t(set_key, set_val)); continue; } if (arg == "-unset" && argidx+1 < args.size()) { @@ -180,5 +175,87 @@ struct SetparamPass : public Pass { } } } SetparamPass; - + +struct ChparamPass : public Pass { + ChparamPass() : Pass("chparam", "re-evaluate modules with new parameters") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" chparam [ -set name value ]... [selection]\n"); + log("\n"); + log("Re-evaluate the selected modules with new parameters. String values must be\n"); + log("passed in double quotes (\").\n"); + log("\n"); + log("\n"); + log(" chparam -list [selection]\n"); + log("\n"); + log("List the available parameters of the selected modules.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + std::vector<setunset_t> setunset_list; + dict<RTLIL::IdString, RTLIL::Const> new_parameters; + bool list_mode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + std::string arg = args[argidx]; + if (arg == "-set" && argidx+2 < args.size()) { + string set_key = args[++argidx]; + string set_val = args[++argidx]; + setunset_list.push_back(setunset_t(set_key, set_val)); + continue; + } + if (arg == "-list") { + list_mode = true; + continue; + } + break; + } + + for (int i = argidx; i < GetSize(args); i++) + if (design->module("$abstract\\" + args[i]) != nullptr && + design->module(RTLIL::escape_id(args[i])) == nullptr) + args[i] = "$abstract\\" + args[i]; + + extra_args(args, argidx, design); + + do_setunset(new_parameters, setunset_list); + + if (list_mode) { + if (!new_parameters.empty()) + log_cmd_error("The options -set and -list cannot be used together.\n"); + for (auto module : design->selected_modules()) { + log("%s:\n", log_id(module)); + for (auto param : module->avail_parameters) + log(" %s\n", log_id(param)); + } + return; + } + + pool<IdString> modnames, old_modnames; + for (auto module : design->selected_whole_modules_warn()) { + modnames.insert(module->name); + old_modnames.insert(module->name); + } + modnames.sort(); + + for (auto modname : modnames) { + Module *module = design->module(modname); + Module *new_module = design->module(module->derive(design, new_parameters)); + if (module != new_module) { + Module *m = new_module->clone(); + m->name = module->name; + design->remove(module); + design->add(m); + } + if (old_modnames.count(new_module->name) == 0) + design->remove(new_module); + } + } +} ChparamPass; + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc index b9a29b7d2..26b2eb87d 100644 --- a/passes/cmds/setundef.cc +++ b/passes/cmds/setundef.cc @@ -2,11 +2,11 @@ * 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 @@ -79,11 +79,15 @@ struct SetundefPass : public Pass { log(" replace with random bits using the specified integer als seed\n"); log(" value for the random number generator.\n"); log("\n"); + log(" -init\n"); + log(" also create/update init values for flip-flops\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { bool got_value = false; bool undriven_mode = false; + bool init_mode = false; SetundefWorker worker; size_t argidx; @@ -103,6 +107,10 @@ struct SetundefPass : public Pass { worker.next_bit_mode = 1; continue; } + if (args[argidx] == "-init") { + init_mode = true; + continue; + } if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) { got_value = true; worker.next_bit_mode = 2; @@ -118,12 +126,8 @@ struct SetundefPass : public Pass { if (!got_value) log_cmd_error("One of the options -zero, -one, or -random <seed> must be specified.\n"); - for (auto &mod_it : design->modules_) + for (auto module : design->selected_modules()) { - RTLIL::Module *module = mod_it.second; - if (!design->selected(module)) - continue; - if (undriven_mode) { if (!module->processes.empty()) @@ -151,9 +155,89 @@ struct SetundefPass : public Pass { } } + if (init_mode) + { + SigMap sigmap(module); + pool<SigBit> ffbits; + pool<Wire*> initwires; + + pool<IdString> fftypes; + fftypes.insert("$dff"); + fftypes.insert("$dffe"); + fftypes.insert("$dffsr"); + fftypes.insert("$adff"); + + std::vector<char> list_np = {'N', 'P'}, list_01 = {'0', '1'}; + + for (auto c1 : list_np) + fftypes.insert(stringf("$_DFF_%c_", c1)); + + for (auto c1 : list_np) + for (auto c2 : list_np) + fftypes.insert(stringf("$_DFFE_%c%c_", c1, c2)); + + for (auto c1 : list_np) + for (auto c2 : list_np) + for (auto c3 : list_01) + fftypes.insert(stringf("$_DFF_%c%c%c_", c1, c2, c3)); + + for (auto c1 : list_np) + for (auto c2 : list_np) + for (auto c3 : list_np) + fftypes.insert(stringf("$_DFFSR_%c%c%c_", c1, c2, c3)); + + for (auto cell : module->cells()) + { + if (!fftypes.count(cell->type)) + continue; + + for (auto bit : sigmap(cell->getPort("\\Q"))) + ffbits.insert(bit); + } + + for (auto wire : module->wires()) + { + if (!wire->attributes.count("\\init")) + continue; + + for (auto bit : sigmap(wire)) + ffbits.erase(bit); + + initwires.insert(wire); + } + + for (int wire_types = 0; wire_types < 2; wire_types++) + for (auto wire : module->wires()) + { + if (wire->name[0] == (wire_types ? '\\' : '$')) + next_wire: + continue; + + for (auto bit : sigmap(wire)) + if (!ffbits.count(bit)) + goto next_wire; + + for (auto bit : sigmap(wire)) + ffbits.erase(bit); + + initwires.insert(wire); + } + + for (auto wire : initwires) + { + Const &initval = wire->attributes["\\init"]; + + for (int i = 0; i < GetSize(wire); i++) + if (GetSize(initval) <= i) + initval.bits.push_back(worker.next_bit()); + else if (initval.bits[i] == State::Sx) + initval.bits[i] = worker.next_bit(); + } + } + module->rewrite_sigspecs(worker); } } } SetundefPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index 63da29b94..87504a33f 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -2,11 +2,11 @@ * 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 @@ -41,7 +41,7 @@ struct ShowWorker { CellTypes ct; - std::vector<std::string> dot_escape_store; + vector<shared_str> dot_escape_store; std::map<RTLIL::IdString, int> dot_id2num_store; std::map<RTLIL::IdString, int> autonames; int single_idx_count; @@ -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()) { @@ -590,6 +590,10 @@ struct ShowPass : public Pass { log(" inputs or outputs. This option can be used multiple times to specify\n"); log(" more than one library.\n"); log("\n"); + log(" note: in most cases it is better to load the library before calling\n"); + log(" show with 'read_verilog -lib <filename>'. it is also possible to\n"); + log(" load liberty files with 'read_liberty -lib <filename>'.\n"); + log("\n"); log(" -prefix <prefix>\n"); log(" generate <prefix>.* instead of ~/.yosys_show.*\n"); log("\n"); @@ -606,7 +610,7 @@ struct ShowPass : public Pass { log(" -colors <seed>\n"); log(" Randomly assign colors to the wires. The integer argument is the seed\n"); log(" for the random number generator. Change the seed value if the colored\n"); - log(" graph still is ambigous. A seed of zero deactivates the coloring.\n"); + log(" graph still is ambiguous. A seed of zero deactivates the coloring.\n"); log("\n"); log(" -colorattr <attribute_name>\n"); log(" Use the specified attribute to assign colors. A unique color is\n"); @@ -616,7 +620,7 @@ struct ShowPass : public Pass { log(" annotate busses with a label indicating the width of the bus.\n"); log("\n"); log(" -signed\n"); - log(" mark ports (A, B) that are declarted as signed (using the [AB]_SIGNED\n"); + log(" mark ports (A, B) that are declared as signed (using the [AB]_SIGNED\n"); log(" cell parameter) with an asterisk next to the port name.\n"); log("\n"); log(" -stretch\n"); @@ -630,7 +634,7 @@ struct ShowPass : public Pass { log(" enumerate objects with internal ($-prefixed) names\n"); log("\n"); log(" -long\n"); - log(" do not abbeviate objects with internal ($-prefixed) names\n"); + log(" do not abbreviate objects with internal ($-prefixed) names\n"); log("\n"); log(" -notitle\n"); log(" do not add the module name as graph title to the dot file\n"); @@ -641,18 +645,26 @@ struct ShowPass : public Pass { log("The generated output files are '~/.yosys_show.dot' and '~/.yosys_show.<format>',\n"); log("unless another prefix is specified using -prefix <prefix>.\n"); log("\n"); + log("Yosys on Windows and YosysJS use different defaults: The output is written\n"); + log("to 'show.dot' in the current directory and new viewer is launched.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Generating Graphviz representation of design.\n"); + log_header(design, "Generating Graphviz representation of design.\n"); log_push(); std::vector<std::pair<std::string, RTLIL::Selection>> color_selections; std::vector<std::pair<std::string, RTLIL::Selection>> label_selections; +#if defined(EMSCRIPTEN) || defined(_WIN32) + std::string format = "dot"; + std::string prefix = "show"; +#else std::string format; - std::string viewer_exe; std::string prefix = stringf("%s/.yosys_show", getenv("HOME") ? getenv("HOME") : "."); +#endif + std::string viewer_exe; std::vector<std::string> libfiles; std::vector<RTLIL::Design*> libs; uint32_t colorSeed = 0; @@ -661,7 +673,7 @@ struct ShowPass : public Pass { bool flag_stretch = false; bool flag_pause = false; bool flag_enum = false; - bool flag_abbeviate = true; + bool flag_abbreviate = true; bool flag_notitle = false; RTLIL::IdString colorattr; @@ -731,12 +743,12 @@ struct ShowPass : public Pass { } if (arg == "-enum") { flag_enum = true; - flag_abbeviate = false; + flag_abbreviate = false; continue; } if (arg == "-long") { flag_enum = false; - flag_abbeviate = false; + flag_abbreviate = false; continue; } if (arg == "-notitle") { @@ -772,7 +784,7 @@ struct ShowPass : public Pass { } if (libs.size() > 0) - log_header("Continuing show pass.\n"); + log_header(design, "Continuing show pass.\n"); std::string dot_file = stringf("%s.dot", prefix.c_str()); std::string out_file = stringf("%s.%s", prefix.c_str(), format.empty() ? "svg" : format.c_str()); @@ -784,7 +796,7 @@ struct ShowPass : public Pass { delete lib; log_cmd_error("Can't open dot file `%s' for writing.\n", dot_file.c_str()); } - ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_signed, flag_stretch, flag_enum, flag_abbeviate, flag_notitle, color_selections, label_selections, colorattr); + ShowWorker worker(f, design, libs, colorSeed, flag_width, flag_signed, flag_stretch, flag_enum, flag_abbreviate, flag_notitle, color_selections, label_selections, colorattr); fclose(f); for (auto lib : libs) @@ -833,5 +845,5 @@ struct ShowPass : public Pass { log_pop(); } } ShowPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/splice.cc b/passes/cmds/splice.cc index 3e0158c5c..7418ec4d2 100644 --- a/passes/cmds/splice.cc +++ b/passes/cmds/splice.cc @@ -2,11 +2,11 @@ * 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 @@ -36,6 +36,8 @@ struct SpliceWorker bool sel_by_wire; bool sel_any_bit; bool no_outputs; + bool do_wires; + std::set<RTLIL::IdString> ports; std::set<RTLIL::IdString> no_ports; @@ -62,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++; @@ -209,23 +211,23 @@ struct SpliceWorker std::vector<std::pair<RTLIL::Wire*, RTLIL::SigSpec>> rework_wires; std::vector<Wire*> mod_wires = module->wires(); - for (auto mod : mod_wires) - if (!no_outputs && mod->port_output) { - if (!design->selected(module, mod)) + for (auto wire : mod_wires) + if ((!no_outputs && wire->port_output) || (do_wires && wire->name[0] == '\\')) { + if (!design->selected(module, wire)) continue; - RTLIL::SigSpec sig = sigmap(mod); + RTLIL::SigSpec sig = sigmap(wire); if (driven_chunks.count(sig) > 0) continue; RTLIL::SigSpec new_sig = get_spliced_signal(sig); if (new_sig != sig) - rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(mod, new_sig)); + rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(wire, new_sig)); } else - if (!mod->port_input) { - RTLIL::SigSpec sig = sigmap(mod); + if (!wire->port_input) { + RTLIL::SigSpec sig = sigmap(wire); if (spliced_signals_cache.count(sig) && spliced_signals_cache.at(sig) != sig) - rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(mod, spliced_signals_cache.at(sig))); + rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(wire, spliced_signals_cache.at(sig))); else if (sliced_signals_cache.count(sig) && sliced_signals_cache.at(sig) != sig) - rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(mod, sliced_signals_cache.at(sig))); + rework_wires.push_back(std::pair<RTLIL::Wire*, RTLIL::SigSpec>(wire, sliced_signals_cache.at(sig))); } for (auto &it : rework_wires) @@ -253,7 +255,7 @@ struct SplicePass : public Pass { log("\n"); log("This command adds $slice and $concat cells to the design to make the splicing\n"); log("of multi-bit signals explicit. This for example is useful for coarse grain\n"); - log("synthesis, where dedidacted hardware is needed to splice signals.\n"); + log("synthesis, where dedicated hardware is needed to splice signals.\n"); log("\n"); log(" -sel_by_cell\n"); log(" only select the cell ports to rewire by the cell. if the selection\n"); @@ -268,6 +270,9 @@ struct SplicePass : public Pass { log(" it is sufficient if the driver of any bit of a cell port is selected.\n"); log(" by default all bits must be selected.\n"); log("\n"); + log(" -wires\n"); + log(" also add $slice and $concat cells to drive otherwise unused wires.\n"); + log("\n"); log(" -no_outputs\n"); log(" do not rewire selected module outputs.\n"); log("\n"); @@ -289,6 +294,7 @@ struct SplicePass : public Pass { bool sel_by_wire = false; bool sel_any_bit = false; bool no_outputs = false; + bool do_wires = false; std::set<RTLIL::IdString> ports, no_ports; size_t argidx; @@ -305,6 +311,10 @@ struct SplicePass : public Pass { sel_any_bit = true; continue; } + if (args[argidx] == "-wires") { + do_wires = true; + continue; + } if (args[argidx] == "-no_outputs") { no_outputs = true; continue; @@ -331,7 +341,7 @@ struct SplicePass : public Pass { if (!ports.empty() && !no_ports.empty()) log_cmd_error("The options -port and -no_port are exclusive!\n"); - log_header("Executing SPLICE pass (creating cells for signal splicing).\n"); + log_header(design, "Executing SPLICE pass (creating cells for signal splicing).\n"); for (auto &mod_it : design->modules_) { @@ -348,11 +358,12 @@ struct SplicePass : public Pass { worker.sel_by_wire = sel_by_wire; worker.sel_any_bit = sel_any_bit; worker.no_outputs = no_outputs; + worker.do_wires = do_wires; worker.ports = ports; worker.no_ports = no_ports; worker.run(); } } } SplicePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc index d4e721a5d..14eeb066f 100644 --- a/passes/cmds/splitnets.cc +++ b/passes/cmds/splitnets.cc @@ -2,11 +2,11 @@ * 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 @@ -50,10 +50,23 @@ struct SplitnetsWorker new_wire_name += format.substr(1, 1); RTLIL::Wire *new_wire = module->addWire(module->uniquify(new_wire_name), width); - new_wire->port_id = wire->port_id; + new_wire->port_id = wire->port_id ? wire->port_id + offset : 0; new_wire->port_input = wire->port_input; new_wire->port_output = wire->port_output; + if (wire->attributes.count("\\src")) + new_wire->attributes["\\src"] = wire->attributes.at("\\src"); + + if (wire->attributes.count("\\keep")) + new_wire->attributes["\\keep"] = wire->attributes.at("\\keep"); + + if (wire->attributes.count("\\init")) { + Const old_init = wire->attributes.at("\\init"), new_init; + for (int i = offset; i < offset+width; i++) + new_init.bits.push_back(i < GetSize(old_init) ? old_init.bits.at(i) : State::Sx); + new_wire->attributes["\\init"] = new_init; + } + std::vector<RTLIL::SigBit> sigvec = RTLIL::SigSpec(new_wire).to_sigbit_vector(); splitmap[wire].insert(splitmap[wire].end(), sigvec.begin(), sigvec.end()); } @@ -96,7 +109,7 @@ struct SplitnetsPass : public Pass { bool flag_driver = false; std::string format = "[]:"; - log_header("Executing SPLITNETS pass (splitting up multi-bit signals).\n"); + log_header(design, "Executing SPLITNETS pass (splitting up multi-bit signals).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -117,14 +130,27 @@ struct SplitnetsPass : public Pass { } extra_args(args, argidx, design); - for (auto &mod_it : design->modules_) - { - RTLIL::Module *module = mod_it.second; - if (!design->selected(module)) - continue; + // 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; + if (flag_ports) + { + int normalized_port_factor = 0; + + for (auto wire : module->wires()) + if (wire->port_id != 0) { + 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()) + wire->port_id *= normalized_port_factor; + } + if (flag_driver) { CellTypes ct(design); @@ -176,14 +202,69 @@ 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); module->remove(delete_wires); - module->fixup_ports(); + 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; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc index d68c57b20..362a0edfc 100644 --- a/passes/cmds/stat.cc +++ b/passes/cmds/stat.cc @@ -2,11 +2,11 @@ * 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 @@ -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"); @@ -185,11 +232,12 @@ struct StatPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Printing statistics.\n"); + log_header(design, "Printing statistics.\n"); 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()); @@ -208,25 +262,22 @@ struct StatPass : public Pass { } extra_args(args, argidx, design); - for (auto &it : design->modules_) + for (auto mod : design->selected_modules()) { - if (!design->selected_module(it.first)) - continue; - if (!top_mod && design->full_selection()) - if (it.second->get_bool_attribute("\\top")) - top_mod = it.second; + if (mod->get_bool_attribute("\\top")) + top_mod = mod; - statdata_t data(design, it.second, width_mode); - mod_stat[it.first] = data; + statdata_t data(design, mod, width_mode, cell_area); + mod_stat[mod->name] = data; log("\n"); - log("=== %s%s ===\n", RTLIL::id2cstr(it.first), design->selected_whole_module(it.first) ? "" : " (partially selected)"); + log("=== %s%s ===\n", RTLIL::id2cstr(mod->name), design->selected_whole_module(mod->name) ? "" : " (partially selected)"); log("\n"); data.log_data(); } - if (top_mod != NULL) + if (top_mod != NULL && GetSize(mod_stat) > 1) { log("\n"); log("=== design hierarchy ===\n"); @@ -242,5 +293,5 @@ struct StatPass : public Pass { log("\n"); } } StatPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/cmds/torder.cc b/passes/cmds/torder.cc new file mode 100644 index 000000000..56223610d --- /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(design, "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/cmds/write_file.cc b/passes/cmds/write_file.cc index 25ec4acc2..b78265933 100644 --- a/passes/cmds/write_file.cc +++ b/passes/cmds/write_file.cc @@ -31,7 +31,7 @@ struct WriteFileFrontend : public Frontend { log("\n"); log(" write_file [options] output_file [input_file]\n"); log("\n"); - log("Write the text fron the input file to the output file.\n"); + log("Write the text from the input file to the output file.\n"); log("\n"); log(" -a\n"); log(" Append to output file (instead of overwriting)\n"); diff --git a/passes/equiv/Makefile.inc b/passes/equiv/Makefile.inc index 548eaca3b..dd7b3be02 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 a6e2f01b4..0494a724f 100644 --- a/passes/equiv/equiv_add.cc +++ b/passes/equiv/equiv_add.cc @@ -2,11 +2,11 @@ * 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 @@ -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 (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 (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()); + } + + 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()); + } + + 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)); - if (!SigSpec::parse(gate_signal, module, args[2])) - log_cmd_error("Error in gate signal: %s\n", args[2].c_str()); + if (gold_cell->input(port) && gate_cell->input(port)) + { + SigSpec combined_sig = module->addWire(NEW_ID, width); - if (!SigSpec::parse_rhs(gate_signal, gold_signal, module, args[1])) - log_cmd_error("Error in gold signal: %s\n", args[1].c_str()); + 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]; + } - log_assert(GetSize(gold_signal) == GetSize(gate_signal)); - SigSpec equiv_signal = module->addWire(NEW_ID, GetSize(gold_signal)); + gold_cell->setPort(port, gold_sig); + gate_cell->setPort(port, gate_sig); + continue; + } - SigMap sigmap(module); - sigmap.apply(gold_signal); - sigmap.apply(gate_signal); + 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; - dict<SigBit, SigBit> to_equiv_bits; - pool<Cell*> added_equiv_cells; + 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]; + } - 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); + 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."); + + 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()); + } - 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); + 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 c5b4eda72..c958c3de4 100644 --- a/passes/equiv/equiv_induct.cc +++ b/passes/equiv/equiv_induct.cc @@ -2,11 +2,11 @@ * 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 @@ -32,7 +32,7 @@ struct EquivInductWorker vector<Cell*> cells; pool<Cell*> workset; - ezDefaultSAT ez; + ezSatPtr ez; SatGen satgen; int max_seq; @@ -43,7 +43,8 @@ struct EquivInductWorker SigPool undriven_signals; EquivInductWorker(Module *module, const pool<Cell*> &unproven_equiv_cells, bool model_undef, int max_seq) : module(module), sigmap(module), - cells(module->selected_cells()), workset(unproven_equiv_cells), satgen(&ez, &sigmap), max_seq(max_seq), success_counter(0) + cells(module->selected_cells()), workset(unproven_equiv_cells), + satgen(ez.get(), &sigmap), max_seq(max_seq), success_counter(0) { satgen.model_undef = model_undef; } @@ -58,14 +59,14 @@ 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); - int cond = ez.IFF(ez_a, ez_b); + int cond = ez->IFF(ez_a, ez_b); if (satgen.model_undef) - cond = ez.OR(cond, satgen.importUndefSigBit(bit_a, step)); + cond = ez->OR(cond, satgen.importUndefSigBit(bit_a, step)); ez_equal_terms.push_back(cond); } } @@ -73,11 +74,11 @@ struct EquivInductWorker if (satgen.model_undef) { for (auto bit : undriven_signals.export_all()) - ez.assume(ez.NOT(satgen.importUndefSigBit(bit, step))); + ez->assume(ez->NOT(satgen.importUndefSigBit(bit, step))); } log_assert(!ez_step_is_consistent.count(step)); - ez_step_is_consistent[step] = ez.expression(ez.OpAnd, ez_equal_terms); + ez_step_is_consistent[step] = ez->expression(ez->OpAnd, ez_equal_terms); } void run() @@ -101,27 +102,27 @@ struct EquivInductWorker if (satgen.model_undef) { for (auto bit : satgen.initial_state.export_all()) - ez.assume(ez.NOT(satgen.importUndefSigBit(bit, 1))); + ez->assume(ez->NOT(satgen.importUndefSigBit(bit, 1))); log(" Undef modelling: force def on %d initial reg values and %d inputs.\n", GetSize(satgen.initial_state), GetSize(undriven_signals)); } for (int step = 1; step <= max_seq; step++) { - ez.assume(ez_step_is_consistent[step]); + ez->assume(ez_step_is_consistent[step]); - log(" Proving existence of base case for step %d. (%d clauses over %d variables)\n", step, ez.numCnfClauses(), ez.numCnfVariables()); - if (!ez.solve()) { + log(" Proving existence of base case for step %d. (%d clauses over %d variables)\n", step, ez->numCnfClauses(), ez->numCnfVariables()); + if (!ez->solve()) { log(" Proof for base case failed. Circuit inherently diverges!\n"); return; } create_timestep(step+1); - int new_step_not_consistent = ez.NOT(ez_step_is_consistent[step+1]); - ez.bind(new_step_not_consistent); + int new_step_not_consistent = ez->NOT(ez_step_is_consistent[step+1]); + ez->bind(new_step_not_consistent); - log(" Proving induction step %d. (%d clauses over %d variables)\n", step, ez.numCnfClauses(), ez.numCnfVariables()); - if (!ez.solve(new_step_not_consistent)) { + log(" Proving induction step %d. (%d clauses over %d variables)\n", step, ez->numCnfClauses(), ez->numCnfVariables()); + if (!ez->solve(new_step_not_consistent)) { log(" Proof for induction step holds. Entire workset of %d cells proven!\n", GetSize(workset)); for (auto cell : workset) cell->setPort("\\B", cell->getPort("\\A")); @@ -136,19 +137,19 @@ 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")))); int ez_a = satgen.importSigBit(bit_a, max_seq+1); int ez_b = satgen.importSigBit(bit_b, max_seq+1); - int cond = ez.XOR(ez_a, ez_b); + int cond = ez->XOR(ez_a, ez_b); if (satgen.model_undef) - cond = ez.AND(cond, ez.NOT(satgen.importUndefSigBit(bit_a, max_seq+1))); + cond = ez->AND(cond, ez->NOT(satgen.importUndefSigBit(bit_a, max_seq+1))); - if (!ez.solve(cond)) { + if (!ez->solve(cond)) { log(" success!\n"); cell->setPort("\\B", cell->getPort("\\A")); success_counter++; @@ -197,7 +198,7 @@ struct EquivInductPass : public Pass { bool model_undef = false; int max_seq = 4; - log_header("Executing EQUIV_INDUCT pass.\n"); + log_header(design, "Executing EQUIV_INDUCT pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc index 5635e7a7e..40ca42621 100644 --- a/passes/equiv/equiv_make.cc +++ b/passes/equiv/equiv_make.cc @@ -2,11 +2,11 @@ * 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 @@ -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) { @@ -407,7 +407,7 @@ struct EquivMakePass : public Pass { log(" Do not match cells or signals that match the names in the file.\n"); log("\n"); log(" -encfile <file>\n"); - log(" Match FSM encodings using the desiption from the file.\n"); + log(" Match FSM encodings using the description from the file.\n"); log(" See 'help fsm_recode' for details.\n"); log("\n"); log("Note: The circuit created by this command is not a miter (with something like\n"); @@ -464,7 +464,7 @@ struct EquivMakePass : public Pass { worker.read_blacklists(); worker.read_encfiles(); - log_header("Executing EQUIV_MAKE pass (creating equiv checking module).\n"); + log_header(design, "Executing EQUIV_MAKE pass (creating equiv checking module).\n"); worker.equiv_mod = design->addModule(RTLIL::escape_id(args[argidx+2])); worker.run(); diff --git a/passes/equiv/equiv_mark.cc b/passes/equiv/equiv_mark.cc new file mode 100644 index 000000000..22c501763 --- /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(design, "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 23b348184..eb2e5a171 100644 --- a/passes/equiv/equiv_miter.cc +++ b/passes/equiv/equiv_miter.cc @@ -2,11 +2,11 @@ * 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 @@ -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); @@ -333,7 +333,7 @@ struct EquivMiterPass : public Pass { found_two_modules: log_cmd_error("Exactly one module must be selected for 'equiv_miter'!\n"); - log_header("Executing EQUIV_MITER pass.\n"); + log_header(design, "Executing EQUIV_MITER pass.\n"); worker.miter_module = design->addModule(worker.miter_name); worker.run(); diff --git a/passes/equiv/equiv_purge.cc b/passes/equiv/equiv_purge.cc new file mode 100644 index 000000000..163b1009b --- /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(design, "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_remove.cc b/passes/equiv/equiv_remove.cc index b6e232f9b..770497a51 100644 --- a/passes/equiv/equiv_remove.cc +++ b/passes/equiv/equiv_remove.cc @@ -2,11 +2,11 @@ * 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 @@ -46,7 +46,7 @@ struct EquivRemovePass : public Pass { bool mode_gate = false; int remove_count = 0; - log_header("Executing EQUIV_REMOVE pass.\n"); + log_header(design, "Executing EQUIV_REMOVE pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/equiv/equiv_simple.cc b/passes/equiv/equiv_simple.cc index 579877e65..49963ed68 100644 --- a/passes/equiv/equiv_simple.cc +++ b/passes/equiv/equiv_simple.cc @@ -2,11 +2,11 @@ * 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 @@ -32,7 +32,7 @@ struct EquivSimpleWorker SigMap &sigmap; dict<SigBit, Cell*> &bit2driver; - ezDefaultSAT ez; + ezSatPtr ez; SatGen satgen; int max_seq; bool verbose; @@ -41,7 +41,7 @@ struct EquivSimpleWorker EquivSimpleWorker(const vector<Cell*> &equiv_cells, SigMap &sigmap, dict<SigBit, Cell*> &bit2driver, int max_seq, bool verbose, bool model_undef) : module(equiv_cells.front()->module), equiv_cells(equiv_cells), equiv_cell(nullptr), - sigmap(sigmap), bit2driver(bit2driver), satgen(&ez, &sigmap), max_seq(max_seq), verbose(verbose) + sigmap(sigmap), bit2driver(bit2driver), satgen(ez.get(), &sigmap), max_seq(max_seq), verbose(verbose) { satgen.model_undef = model_undef; } @@ -89,9 +89,9 @@ 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(); - int ez_context = ez.frozen_literal(); + 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) { @@ -99,14 +99,14 @@ struct EquivSimpleWorker int ez_b = satgen.importDefSigBit(bit_b, max_seq+1); int ez_undef_a = satgen.importUndefSigBit(bit_a, max_seq+1); - ez.assume(ez.XOR(ez_a, ez_b), ez_context); - ez.assume(ez.NOT(ez_undef_a), ez_context); + ez->assume(ez->XOR(ez_a, ez_b), ez_context); + ez->assume(ez->NOT(ez_undef_a), ez_context); } else { int ez_a = satgen.importSigBit(bit_a, max_seq+1); int ez_b = satgen.importSigBit(bit_b, max_seq+1); - ez.assume(ez.XOR(ez_a, ez_b), ez_context); + ez->assume(ez->XOR(ez_a, ez_b), ez_context); } pool<SigBit> seed_a = { bit_a }; @@ -168,16 +168,16 @@ struct EquivSimpleWorker if (satgen.model_undef) { for (auto bit : input_bits) - ez.assume(ez.NOT(satgen.importUndefSigBit(bit, step+1))); + ez->assume(ez->NOT(satgen.importUndefSigBit(bit, step+1))); } if (verbose) - log(" Problem size at t=%d: %d literals, %d clauses\n", step, ez.numCnfVariables(), ez.numCnfClauses()); + log(" Problem size at t=%d: %d literals, %d clauses\n", step, ez->numCnfVariables(), ez->numCnfClauses()); - if (!ez.solve(ez_context)) { + if (!ez->solve(ez_context)) { log(verbose ? " Proved equivalence! Marking $equiv cell as proven.\n" : " success!\n"); equiv_cell->setPort("\\B", equiv_cell->getPort("\\A")); - ez.assume(ez.NOT(ez_context)); + ez->assume(ez->NOT(ez_context)); return true; } @@ -224,7 +224,7 @@ struct EquivSimpleWorker if (!verbose) log(" failed.\n"); - ez.assume(ez.NOT(ez_context)); + ez->assume(ez->NOT(ez_context)); return false; } @@ -277,7 +277,7 @@ struct EquivSimplePass : public Pass { int success_counter = 0; int max_seq = 1; - log_header("Executing EQUIV_SIMPLE pass.\n"); + log_header(design, "Executing EQUIV_SIMPLE pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -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_status.cc b/passes/equiv/equiv_status.cc index 8ca1aacd5..7b9230b35 100644 --- a/passes/equiv/equiv_status.cc +++ b/passes/equiv/equiv_status.cc @@ -2,11 +2,11 @@ * 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 @@ -41,7 +41,7 @@ struct EquivStatusPass : public Pass { bool assert_mode = false; int unproven_count = 0; - log_header("Executing EQUIV_STATUS pass.\n"); + log_header(design, "Executing EQUIV_STATUS pass.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/equiv/equiv_struct.cc b/passes/equiv/equiv_struct.cc new file mode 100644 index 000000000..c4ced6a71 --- /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(design, "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.cc b/passes/fsm/fsm.cc index e76be40c2..3b537ecd8 100644 --- a/passes/fsm/fsm.cc +++ b/passes/fsm/fsm.cc @@ -2,11 +2,11 @@ * 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 @@ -34,7 +34,7 @@ struct FsmPass : public Pass { log(" fsm [options] [selection]\n"); log("\n"); log("This pass calls all the other fsm_* passes in a useful order. This performs\n"); - log("FSM extraction and optimiziation. It also calls opt_clean as needed:\n"); + log("FSM extraction and optimization. It also calls opt_clean as needed:\n"); log("\n"); log(" fsm_detect unless got option -nodetect\n"); log(" fsm_extract\n"); @@ -59,7 +59,7 @@ struct FsmPass : public Pass { log(" -expand, -norecode, -export, -nomap\n"); log(" enable or disable passes as indicated above\n"); log("\n"); - log(" -encoding tye\n"); + log(" -encoding type\n"); log(" -fm_set_fsm_file file\n"); log(" -encfile file\n"); log(" passed through to fsm_recode pass\n"); @@ -76,7 +76,7 @@ struct FsmPass : public Pass { std::string encfile_opt; std::string encoding_opt; - log_header("Executing FSM pass (extract and optimize FSM).\n"); + log_header(design, "Executing FSM pass (extract and optimize FSM).\n"); log_push(); size_t argidx; @@ -145,5 +145,5 @@ struct FsmPass : public Pass { log_pop(); } } FsmPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc index c89553c6b..5a240ba60 100644 --- a/passes/fsm/fsm_detect.cc +++ b/passes/fsm/fsm_detect.cc @@ -2,11 +2,11 @@ * 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 @@ -34,7 +34,7 @@ static SigSet<sig2driver_entry_t> sig2driver, sig2user; static std::set<RTLIL::Cell*> muxtree_cells; static SigPool sig_at_port; -static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, SigPool &recursion_monitor) +static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor) { if (sig_at_port.check_any(assign_map(sig))) return false; @@ -42,31 +42,39 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, Sig if (sig.is_fully_const() || old_sig == sig) return true; - if (recursion_monitor.check_any(sig)) { - log_warning("logic loop in mux tree at signal %s in module %s.\n", - log_signal(sig), RTLIL::id2cstr(module->name)); - return false; - } - - recursion_monitor.add(sig); - std::set<sig2driver_entry_t> cellport_list; sig2driver.find(sig, cellport_list); - for (auto &cellport : cellport_list) { + for (auto &cellport : cellport_list) + { if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") return false; + + if (recursion_monitor.count(cellport.first)) { + log_warning("logic loop in mux tree at signal %s in module %s.\n", + log_signal(sig), RTLIL::id2cstr(module->name)); + return false; + } + + recursion_monitor.insert(cellport.first); + RTLIL::SigSpec sig_a = assign_map(cellport.first->getPort("\\A")); RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B")); - if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) + + if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) { + recursion_monitor.erase(cellport.first); return false; + } + for (int i = 0; i < sig_b.size(); i += sig_a.size()) - if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) + if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) { + recursion_monitor.erase(cellport.first); return false; + } + + recursion_monitor.erase(cellport.first); muxtree_cells.insert(cellport.first); } - recursion_monitor.del(sig); - return true; } @@ -81,6 +89,8 @@ static bool check_state_users(RTLIL::SigSpec sig) RTLIL::Cell *cell = cellport.first; if (muxtree_cells.count(cell) > 0) continue; + if (cell->type == "$logic_not" && assign_map(cell->getPort("\\A")) == sig) + continue; if (cellport.second != "\\A" && cellport.second != "\\B") return false; if (!cell->hasPort("\\A") || !cell->hasPort("\\B") || !cell->hasPort("\\Y")) @@ -100,6 +110,8 @@ static bool check_state_users(RTLIL::SigSpec sig) static void detect_fsm(RTLIL::Wire *wire) { + if (wire->attributes.count("\\init") > 0) + return; if (wire->attributes.count("\\fsm_encoding") > 0 || wire->width <= 1) return; if (sig_at_port.check_any(assign_map(RTLIL::SigSpec(wire)))) @@ -111,7 +123,7 @@ static void detect_fsm(RTLIL::Wire *wire) if ((cellport.first->type != "$dff" && cellport.first->type != "$adff") || cellport.second != "\\Q") continue; muxtree_cells.clear(); - SigPool recursion_monitor; + pool<Cell*> recursion_monitor; RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q")); RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D")); if (sig_q == RTLIL::SigSpec(wire) && check_state_mux_tree(sig_q, sig_d, recursion_monitor) && check_state_users(sig_q)) { @@ -142,7 +154,7 @@ struct FsmDetectPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_DETECT pass (finding FSMs in design).\n"); + log_header(design, "Executing FSM_DETECT pass (finding FSMs in design).\n"); extra_args(args, 1, design); CellTypes ct; @@ -191,5 +203,5 @@ struct FsmDetectPass : public Pass { muxtree_cells.clear(); } } FsmDetectPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_expand.cc b/passes/fsm/fsm_expand.cc index a261eb22b..3ded3f377 100644 --- a/passes/fsm/fsm_expand.cc +++ b/passes/fsm/fsm_expand.cc @@ -2,11 +2,11 @@ * 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 @@ -253,12 +253,12 @@ struct FsmExpandPass : public Pass { log("\n"); log("The fsm_extract pass is conservative about the cells that belong to a finite\n"); log("state machine. This pass can be used to merge additional auxiliary gates into\n"); - log("the finate state machine.\n"); + log("the finite state machine.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_EXPAND pass (merging auxiliary logic into FSMs).\n"); + log_header(design, "Executing FSM_EXPAND pass (merging auxiliary logic into FSMs).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) { @@ -275,5 +275,5 @@ struct FsmExpandPass : public Pass { } } } FsmExpandPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_export.cc b/passes/fsm/fsm_export.cc index ad9270334..1cbfcfae8 100644 --- a/passes/fsm/fsm_export.cc +++ b/passes/fsm/fsm_export.cc @@ -3,11 +3,11 @@ * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> * Copyright (C) 2012 Martin Schmölzer <martin@schmoelzer.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 @@ -152,7 +152,7 @@ struct FsmExportPass : public Pass { bool flag_origenc = false; size_t argidx; - log_header("Executing FSM_EXPORT pass (exporting FSMs in KISS2 file format).\n"); + log_header(design, "Executing FSM_EXPORT pass (exporting FSMs in KISS2 file format).\n"); for (argidx = 1; argidx < args.size(); argidx++) { arg = args[argidx]; diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc index 68667ef02..95cb498e3 100644 --- a/passes/fsm/fsm_extract.cc +++ b/passes/fsm/fsm_extract.cc @@ -2,11 +2,11 @@ * 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 @@ -56,6 +56,17 @@ static bool find_states(RTLIL::SigSpec sig, const RTLIL::SigSpec &dff_out, RTLIL std::set<sig2driver_entry_t> cellport_list; sig2driver.find(sig, cellport_list); + + if (GetSize(cellport_list) > 1) { + log(" found %d combined drivers for state signal %s.\n", GetSize(cellport_list), log_signal(sig)); + return false; + } + + if (GetSize(cellport_list) < 1) { + log(" found no driver for state signal %s.\n", log_signal(sig)); + return false; + } + for (auto &cellport : cellport_list) { RTLIL::Cell *cell = module->cells_.at(cellport.first); @@ -90,9 +101,11 @@ static bool find_states(RTLIL::SigSpec sig, const RTLIL::SigSpec &dff_out, RTLIL log(" found reset state: %s (guessed from mux tree)\n", log_signal(*reset_state)); } while (0); - if (ctrl.extract(sig_s).size() == 0) { - log(" found ctrl input: %s\n", log_signal(sig_s)); - ctrl.append(sig_s); + for (auto sig_s_bit : sig_s) { + if (ctrl.extract(sig_s_bit).empty()) { + log(" found ctrl input: %s\n", log_signal(sig_s_bit)); + ctrl.append(sig_s_bit); + } } if (!find_states(sig_aa, dff_out, ctrl, states)) @@ -241,7 +254,7 @@ static void extract_fsm(RTLIL::Wire *wire) { log("Extracting FSM `%s' from module `%s'.\n", wire->name.c_str(), module->name.c_str()); - // get input and output signals for state ff + // get input and output signals for state ff RTLIL::SigSpec dff_out = assign_map(RTLIL::SigSpec(wire)); RTLIL::SigSpec dff_in(RTLIL::State::Sm, wire->width); @@ -305,7 +318,9 @@ static void extract_fsm(RTLIL::Wire *wire) for (auto &cellport : cellport_list) { RTLIL::Cell *cell = module->cells_.at(cellport.first); RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); - RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B")); + RTLIL::SigSpec sig_b; + if (cell->hasPort("\\B")) + sig_b = assign_map(cell->getPort("\\B")); RTLIL::SigSpec sig_y = assign_map(cell->getPort("\\Y")); if (cellport.second == "\\A" && !sig_b.is_fully_const()) continue; @@ -401,7 +416,7 @@ struct FsmExtractPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_EXTRACT pass (extracting FSM from design).\n"); + log_header(design, "Executing FSM_EXTRACT pass (extracting FSM from design).\n"); extra_args(args, 1, design); CellTypes ct; @@ -458,5 +473,5 @@ struct FsmExtractPass : public Pass { sig2trigger.clear(); } } FsmExtractPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_info.cc b/passes/fsm/fsm_info.cc index 4a1f1d9a2..2cc1a7d53 100644 --- a/passes/fsm/fsm_info.cc +++ b/passes/fsm/fsm_info.cc @@ -2,11 +2,11 @@ * 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 @@ -43,7 +43,7 @@ struct FsmInfoPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_INFO pass (dumping all available information on FSM cells).\n"); + log_header(design, "Executing FSM_INFO pass (dumping all available information on FSM cells).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) @@ -58,5 +58,5 @@ struct FsmInfoPass : public Pass { } } } FsmInfoPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_map.cc b/passes/fsm/fsm_map.cc index 155801a3a..5b32ed599 100644 --- a/passes/fsm/fsm_map.cc +++ b/passes/fsm/fsm_map.cc @@ -2,11 +2,11 @@ * 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 @@ -335,7 +335,7 @@ struct FsmMapPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_MAP pass (mapping FSMs to basic logic).\n"); + log_header(design, "Executing FSM_MAP pass (mapping FSMs to basic logic).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) { @@ -350,5 +350,5 @@ struct FsmMapPass : public Pass { } } } FsmMapPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc index 4b93d79f9..5b1da44fc 100644 --- a/passes/fsm/fsm_opt.cc +++ b/passes/fsm/fsm_opt.cc @@ -2,11 +2,11 @@ * 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 @@ -75,14 +75,14 @@ struct FsmOpt fsm_data.reset_state = old_to_new_state.at(fsm_data.reset_state); } } - + 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; - + char *str = strdup(bit.wire->attributes["\\unused_bits"].decode_string().c_str()); for (char *tok = strtok(str, " "); tok != NULL; tok = strtok(NULL, " ")) { if (tok[0] && bit.offset == atoi(tok)) { @@ -336,7 +336,7 @@ struct FsmOptPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FSM_OPT pass (simple optimizations of FSMs).\n"); + log_header(design, "Executing FSM_OPT pass (simple optimizations of FSMs).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) { @@ -347,5 +347,5 @@ struct FsmOptPass : public Pass { } } } FsmOptPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsm_recode.cc b/passes/fsm/fsm_recode.cc index 169968103..5102d8334 100644 --- a/passes/fsm/fsm_recode.cc +++ b/passes/fsm/fsm_recode.cc @@ -2,11 +2,11 @@ * 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 @@ -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; @@ -93,7 +93,7 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs fsm_data.state_bits = new_num_state_bits; } else log_error("FSM encoding `%s' is not supported!\n", encoding.c_str()); - + if (encfile) fprintf(encfile, ".fsm %s %s\n", log_id(module), RTLIL::unescape_id(cell->parameters["\\NAME"].decode_string()).c_str()); @@ -134,7 +134,7 @@ struct FsmRecodePass : public Pass { log("\n"); log("This pass reassign the state encodings for FSM cells. At the moment only\n"); log("one-hot encoding and binary encoding is supported.\n"); - + log(" -encoding <type>\n"); log(" specify the encoding scheme used for FSMs without the\n"); log(" 'fsm_encoding' attribute or with the attribute set to `auto'.\n"); @@ -157,7 +157,7 @@ struct FsmRecodePass : public Pass { FILE *encfile = NULL; std::string default_encoding; - log_header("Executing FSM_RECODE pass (re-assigning FSM state encoding).\n"); + log_header(design, "Executing FSM_RECODE pass (re-assigning FSM state encoding).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; @@ -193,5 +193,5 @@ struct FsmRecodePass : public Pass { fclose(encfile); } } FsmRecodePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/fsm/fsmdata.h b/passes/fsm/fsmdata.h index 5671d0006..68222769a 100644 --- a/passes/fsm/fsmdata.h +++ b/passes/fsm/fsmdata.h @@ -2,11 +2,11 @@ * 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 @@ -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 99aa1e116..1fb669c11 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 58b796a62..94b93de5d 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -2,11 +2,11 @@ * 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 @@ -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; } @@ -261,14 +261,14 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check return did_something; } -void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*> &used, RTLIL::Module *mod, int indent) +void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> &used, RTLIL::Module *mod, int indent) { if (used.count(mod) > 0) return; if (indent == 0) log("Top module: %s\n", mod->name.c_str()); - else + else if (!mod->get_bool_attribute("\\blackbox")) log("Used module: %*s%s\n", indent, "", mod->name.c_str()); used.insert(mod); @@ -285,9 +285,9 @@ void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*> &used, RTL } } -void hierarchy(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib, bool first_pass) +void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib) { - std::set<RTLIL::Module*> used; + std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> used; hierarchy_worker(design, used, top, 0); std::vector<RTLIL::Module*> del_modules; @@ -295,17 +295,17 @@ void hierarchy(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib, bool f if (used.count(it.second) == 0) del_modules.push_back(it.second); + int del_counter = 0; for (auto mod : del_modules) { - if (first_pass && mod->name.substr(0, 9) == "$abstract") - continue; if (!purge_lib && mod->get_bool_attribute("\\blackbox")) continue; log("Removing unused module `%s'.\n", mod->name.c_str()); design->modules_.erase(mod->name); + del_counter++; delete mod; } - log("Removed %d unused modules.\n", GetSize(del_modules)); + log("Removed %d unused modules.\n", del_counter); } bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod) @@ -313,12 +313,23 @@ bool set_keep_assert(std::map<RTLIL::Module*, bool> &cache, RTLIL::Module *mod) if (cache.count(mod) == 0) for (auto c : mod->cells()) { RTLIL::Module *m = mod->design->module(c->type); - if ((m != nullptr && set_keep_assert(cache, m)) || c->type == "$assert") + if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in("$assert", "$assume")) return cache[mod] = true; } return cache[mod]; } +int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db) +{ + if (db.count(module) == 0) { + db[module] = 0; + for (auto cell : module->cells()) + if (design->module(cell->type)) + db[module] = max(db[module], find_top_mod_score(design, design->module(cell->type), db) + 1); + } + return db.at(module); +} + struct HierarchyPass : public Pass { HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { } virtual void help() @@ -339,7 +350,7 @@ struct HierarchyPass : public Pass { log("\n"); log(" -purge_lib\n"); log(" by default the hierarchy command will not remove library (blackbox)\n"); - log(" module. use this options to also remove unused blackbox modules.\n"); + log(" modules. use this option to also remove unused blackbox modules.\n"); log("\n"); log(" -libdir <directory>\n"); log(" search for files named <module_name>.v in the specified directory\n"); @@ -363,6 +374,9 @@ struct HierarchyPass : public Pass { log(" specified top module. otherwise a module with the 'top' attribute set\n"); log(" will implicitly be used as top module, if such a module exists.\n"); log("\n"); + log(" -auto-top\n"); + log(" automatically determine the top of the design hierarchy and mark it.\n"); + log("\n"); log("In -generate mode this pass generates blackbox modules for the given cell\n"); log("types (wildcards supported). For this the design is searched for cells that\n"); log("match the given types and then the given port declarations are used to\n"); @@ -372,7 +386,7 @@ struct HierarchyPass : public Pass { log("\n"); log("Input ports are specified with the 'i' prefix, output ports with the 'o'\n"); log("prefix and inout ports with the 'io' prefix. The optional <num> specifies\n"); - log("the position of the port in the parameter list (needed when instanciated\n"); + log("the position of the port in the parameter list (needed when instantiated\n"); log("using positional arguments). When <num> is not specified, the <portname> can\n"); log("also contain wildcard characters.\n"); log("\n"); @@ -382,13 +396,14 @@ struct HierarchyPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing HIERARCHY pass (managing design hierarchy).\n"); + log_header(design, "Executing HIERARCHY pass (managing design hierarchy).\n"); bool flag_check = false; bool purge_lib = false; RTLIL::Module *top_mod = NULL; std::vector<std::string> libdirs; + bool auto_top_mode = false; bool generate_mode = false; bool keep_positionals = false; bool nokeep_asserts = false; @@ -470,6 +485,10 @@ struct HierarchyPass : public Pass { log_cmd_error("Module `%s' not found!\n", args[argidx].c_str()); continue; } + if (args[argidx] == "-auto-top") { + auto_top_mode = true; + continue; + } break; } extra_args(args, argidx, design, false); @@ -481,35 +500,47 @@ struct HierarchyPass : public Pass { log_push(); - if (top_mod == NULL) + if (top_mod == nullptr) for (auto &mod_it : design->modules_) if (mod_it.second->get_bool_attribute("\\top")) top_mod = mod_it.second; - if (top_mod != NULL) - hierarchy(design, top_mod, purge_lib, true); + if (top_mod == nullptr && auto_top_mode) { + log_header(design, "Finding top of design hierarchy..\n"); + dict<Module*, int> db; + for (Module *mod : design->selected_modules()) { + int score = find_top_mod_score(design, mod, db); + log("root of %3d design levels: %-20s\n", score, log_id(mod)); + if (!top_mod || score > db[top_mod]) + top_mod = mod; + } + if (top_mod != nullptr) + log("Automatically selected %s as design top module.\n", log_id(top_mod)); + } bool did_something = true; - bool did_something_once = false; - while (did_something) { + while (did_something) + { did_something = false; - std::vector<RTLIL::IdString> modnames; - modnames.reserve(design->modules_.size()); - for (auto &mod_it : design->modules_) - modnames.push_back(mod_it.first); - for (auto &modname : modnames) { - if (design->modules_.count(modname) == 0) - continue; - if (expand_module(design, design->modules_[modname], flag_check, libdirs)) + + std::set<RTLIL::Module*, IdString::compare_ptr_by_name<Module>> used_modules; + if (top_mod != NULL) { + log_header(design, "Analyzing design hierarchy..\n"); + hierarchy_worker(design, used_modules, top_mod, 0); + } else { + for (auto mod : design->modules()) + used_modules.insert(mod); + } + + for (auto module : used_modules) { + if (expand_module(design, module, flag_check, libdirs)) did_something = true; } - if (did_something) - did_something_once = true; } - if (top_mod != NULL && did_something_once) { - log_header("Re-running hierarchy analysis..\n"); - hierarchy(design, top_mod, purge_lib, false); + if (top_mod != NULL) { + log_header(design, "Analyzing design hierarchy..\n"); + hierarchy_clean(design, top_mod, purge_lib); } if (top_mod != NULL) { @@ -580,5 +611,5 @@ struct HierarchyPass : public Pass { log_pop(); } } HierarchyPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/hierarchy/singleton.cc b/passes/hierarchy/singleton.cc new file mode 100644 index 000000000..03c365fb5 --- /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(design, "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 8d4012c53..9f312f82d 100644 --- a/passes/hierarchy/submod.cc +++ b/passes/hierarchy/submod.cc @@ -2,11 +2,11 @@ * 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 @@ -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,19 +285,24 @@ 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) { - log_header("Executing SUBMOD pass (moving cells to submodules as requested).\n"); + log_header(design, "Executing SUBMOD pass (moving cells to submodules as requested).\n"); 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); @@ -306,7 +321,7 @@ struct SubmodPass : public Pass { if (opt_name.empty()) { Pass::call(design, "opt_clean"); - log_header("Continuing SUBMOD pass.\n"); + log_header(design, "Continuing SUBMOD pass.\n"); std::set<RTLIL::IdString> handled_modules; @@ -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; } @@ -341,13 +356,13 @@ struct SubmodPass : public Pass { log("Nothing selected -> do nothing.\n"); else { Pass::call_on_module(design, module, "opt_clean"); - log_header("Continuing SUBMOD pass.\n"); - SubmodWorker worker(design, module, opt_name); + log_header(design, "Continuing SUBMOD pass.\n"); + SubmodWorker worker(design, module, copy_mode, opt_name); } } log_pop(); } } SubmodPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory.cc b/passes/memory/memory.cc index 866efae77..e3c627607 100644 --- a/passes/memory/memory.cc +++ b/passes/memory/memory.cc @@ -2,11 +2,11 @@ * 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 @@ -31,11 +31,11 @@ struct MemoryPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" memory [-nomap] [-bram <bram_rules>] [selection]\n"); + log(" memory [-nomap] [-nordff] [-bram <bram_rules>] [selection]\n"); log("\n"); log("This pass calls all the other memory_* passes in a useful order:\n"); log("\n"); - log(" memory_dff\n"); + log(" memory_dff [-nordff]\n"); log(" opt_clean\n"); log(" memory_share\n"); log(" opt_clean\n"); @@ -50,9 +50,10 @@ struct MemoryPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { bool flag_nomap = false; + bool flag_nordff = false; string memory_bram_opts; - log_header("Executing MEMORY pass.\n"); + log_header(design, "Executing MEMORY pass.\n"); log_push(); size_t argidx; @@ -61,6 +62,10 @@ struct MemoryPass : public Pass { flag_nomap = true; continue; } + if (args[argidx] == "-nordff") { + flag_nordff = true; + continue; + } if (argidx+1 < args.size() && args[argidx] == "-bram") { memory_bram_opts += " -rules " + args[++argidx]; continue; @@ -69,7 +74,7 @@ struct MemoryPass : public Pass { } extra_args(args, argidx, design); - Pass::call(design, "memory_dff"); + Pass::call(design, flag_nordff ? "memory_dff -nordff" : "memory_dff"); Pass::call(design, "opt_clean"); Pass::call(design, "memory_share"); Pass::call(design, "opt_clean"); @@ -84,5 +89,5 @@ struct MemoryPass : public Pass { log_pop(); } } MemoryPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc index 8f4214027..7b5dd08ab 100644 --- a/passes/memory/memory_bram.cc +++ b/passes/memory/memory_bram.cc @@ -2,11 +2,11 @@ * 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 @@ -32,6 +32,7 @@ struct rules_t SigSpec sig_addr, sig_data, sig_en; bool effective_clkpol; bool make_transp; + bool make_outreg; int mapped_port; }; @@ -85,6 +86,7 @@ struct rules_t pi.clkpol = clkpol[i]; pi.mapped_port = -1; pi.make_transp = false; + pi.make_outreg = false; pi.effective_clkpol = false; portinfos.push_back(pi); } @@ -110,15 +112,15 @@ struct rules_t if (ports[i] != other.ports[i]) log_error("Bram %s variants %d and %d have different number of %c-ports.\n", log_id(name), variant, other.variant, 'A'+i); if (wrmode[i] != other.wrmode[i]) - variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[1]; + variant_params[stringf("\\CFG_WRMODE_%c", 'A' + i)] = wrmode[i]; if (enable[i] != other.enable[i]) - variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[1]; + variant_params[stringf("\\CFG_ENABLE_%c", 'A' + i)] = enable[i]; if (transp[i] != other.transp[i]) - variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[1]; + variant_params[stringf("\\CFG_TRANSP_%c", 'A' + i)] = transp[i]; if (clocks[i] != other.clocks[i]) - variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[1]; + variant_params[stringf("\\CFG_CLOCKS_%c", 'A' + i)] = clocks[i]; if (clkpol[i] != other.clkpol[i]) - variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[1]; + variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[i]; } } }; @@ -126,7 +128,7 @@ struct rules_t struct match_t { IdString name; dict<string, int> min_limits, max_limits; - bool or_next_if_better, make_transp; + bool or_next_if_better, make_transp, make_outreg; char shuffle_enable; }; @@ -277,6 +279,7 @@ struct rules_t data.name = RTLIL::escape_id(tokens[1]); data.or_next_if_better = false; data.make_transp = false; + data.make_outreg = false; data.shuffle_enable = 0; while (next_line()) @@ -309,6 +312,12 @@ struct rules_t continue; } + if (GetSize(tokens) == 1 && tokens[0] == "make_outreg") { + data.make_transp = true; + data.make_outreg = true; + continue; + } + if (GetSize(tokens) == 1 && tokens[0] == "or_next_if_better") { data.or_next_if_better = true; continue; @@ -320,9 +329,7 @@ struct rules_t void parse(string filename) { - if (filename.substr(0, 2) == "+/") - filename = proc_share_dirname() + filename.substr(1); - + rewrite_filename(filename); infile.open(filename); linecount = 0; @@ -380,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); @@ -393,6 +400,16 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, int mem_width = cell->getParam("\\WIDTH").as_int(); // int mem_offset = cell->getParam("\\OFFSET").as_int(); + bool cell_init = !SigSpec(cell->getParam("\\INIT")).is_fully_undef(); + vector<Const> initdata; + + if (cell_init) { + Const initparam = cell->getParam("\\INIT"); + initdata.reserve(mem_size); + for (int i=0; i < mem_size; i++) + initdata.push_back(initparam.extract(mem_width*i, mem_width, State::Sx)); + } + int wr_ports = cell->getParam("\\WR_PORTS").as_int(); auto wr_clken = SigSpec(cell->getParam("\\WR_CLK_ENABLE")); auto wr_clkpol = SigSpec(cell->getParam("\\WR_CLK_POLARITY")); @@ -412,11 +429,12 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, rd_clkpol.extend_u0(rd_ports); rd_transp.extend_u0(rd_ports); + SigSpec rd_en = cell->getPort("\\RD_EN"); SigSpec rd_clk = cell->getPort("\\RD_CLK"); SigSpec rd_data = cell->getPort("\\RD_DATA"); SigSpec rd_addr = cell->getPort("\\RD_ADDR"); - if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0) + if (match.shuffle_enable && bram.dbits >= portinfos.at(match.shuffle_enable - 'A').enable*2 && portinfos.at(match.shuffle_enable - 'A').enable > 0 && wr_ports > 0) { int bucket_size = bram.dbits / portinfos.at(match.shuffle_enable - 'A').enable; log(" Shuffle bit order to accommodate enable buckets of size %d..\n", bucket_size); @@ -588,7 +606,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, mapped_wr_port:; } - // houskeeping stuff for growing more read ports and restarting read port assignments + // housekeeping stuff for growing more read ports and restarting read port assignments int grow_read_ports_cursor = -1; bool try_growing_more_read_ports = false; @@ -656,6 +674,10 @@ grow_read_ports:; if (clken) { if (pi.clocks == 0) { + if (match.make_outreg) { + pi.make_outreg = true; + goto skip_bram_rport_clkcheck; + } log(" Bram port %c%d.%d has incompatible clock type.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); goto skip_bram_rport; } @@ -667,12 +689,17 @@ grow_read_ports:; log(" Bram port %c%d.%d has incompatible clock polarity.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); goto skip_bram_rport; } + if (rd_en[cell_port_i] != State::S1 && pi.enable == 0) { + log(" Bram port %c%d.%d has no read enable input.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); + goto skip_bram_rport; + } + skip_bram_rport_clkcheck: if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) { if (match.make_transp && wr_ports <= 1) { pi.make_transp = true; enable_make_transp = true; } else { - log(" Bram port %c%d.%d has incompatible read transparancy.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); + log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); goto skip_bram_rport; } } @@ -691,6 +718,7 @@ grow_read_ports:; clock_polarities[pi.clkpol] = clkdom.second; read_transp[pi.transp] = transp; pi.sig_clock = clkdom.first; + pi.sig_en = rd_en[cell_port_i]; pi.effective_clkpol = clkdom.second; } @@ -789,6 +817,22 @@ grow_read_ports:; for (auto &vp : variant_params) c->setParam(vp.first, vp.second); + if (cell_init) { + int init_offset = grid_a*(1 << bram.abits); + int init_shift = grid_d*bram.dbits; + int init_size = (1 << bram.abits); + Const initparam(State::Sx, init_size*bram.dbits); + for (int i = 0; i < init_size; i++) { + State padding = State::Sx; + for (int j = 0; j < bram.dbits; j++) + if (init_offset+i < GetSize(initdata) && init_shift+j < GetSize(initdata[init_offset+i])) + initparam[i*bram.dbits+j] = initdata[init_offset+i][init_shift+j]; + else + initparam[i*bram.dbits+j] = padding; + } + c->setParam("\\INIT", initparam); + } + for (auto &pi : portinfos) { if (pi.dupidx != dupidx) @@ -815,8 +859,11 @@ grow_read_ports:; if (pi.enable) { SigSpec sig_en = pi.sig_en; - sig_en.extend_u0((grid_d+1) * pi.enable); - sig_en = sig_en.extract(grid_d * pi.enable, pi.enable); + + if (pi.wrmode == 1) { + sig_en.extend_u0((grid_d+1) * pi.enable); + sig_en = sig_en.extract(grid_d * pi.enable, pi.enable); + } if (!addr_ok.empty()) sig_en = module->Mux(NEW_ID, SigSpec(0, GetSize(sig_en)), sig_en, addr_ok); @@ -846,6 +893,14 @@ grow_read_ports:; SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits); c->setPort(stringf("\\%sDATA", pf), bram_dout); + if (pi.make_outreg) { + SigSpec bram_dout_q = module->addWire(NEW_ID, bram.dbits); + if (!pi.sig_en.empty()) + bram_dout = module->Mux(NEW_ID, bram_dout_q, bram_dout, pi.sig_en); + module->addDff(NEW_ID, pi.sig_clock, bram_dout, bram_dout_q, pi.effective_clkpol); + bram_dout = bram_dout_q; + } + if (pi.make_transp) { log(" Adding extra logic for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1); @@ -871,7 +926,7 @@ grow_read_ports:; } SigSpec addr_ok_q = addr_ok; - if (pi.clocks && !addr_ok.empty()) { + if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) { addr_ok_q = module->addWire(NEW_ID); module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol); } @@ -905,6 +960,8 @@ void handle_cell(Cell *cell, const rules_t &rules) { log("Processing %s.%s:\n", log_id(cell->module), log_id(cell)); + bool cell_init = !SigSpec(cell->getParam("\\INIT")).is_fully_undef(); + dict<string, int> match_properties; match_properties["words"] = cell->getParam("\\SIZE").as_int(); match_properties["abits"] = cell->getParam("\\ABITS").as_int(); @@ -920,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++) { @@ -975,6 +1032,12 @@ void handle_cell(Cell *cell, const rules_t &rules) log(" Metrics for %s: awaste=%d dwaste=%d bwaste=%d waste=%d efficiency=%d\n", log_id(match.name), awaste, dwaste, bwaste, waste, efficiency); + if (cell_init && bram.init == 0) { + log(" Rule #%d for bram type %s (variant %d) rejected: cannot be initialized.\n", + i+1, log_id(bram.name), bram.variant); + goto next_match_rule; + } + for (auto it : match.min_limits) { if (it.first == "waste" || it.first == "dups" || it.first == "acells" || it.first == "dcells" || it.first == "cells") continue; @@ -987,6 +1050,7 @@ void handle_cell(Cell *cell, const rules_t &rules) i+1, log_id(bram.name), bram.variant, it.first.c_str(), it.second); goto next_match_rule; } + for (auto it : match.max_limits) { if (it.first == "acells" || it.first == "dcells" || it.first == "cells") continue; @@ -1014,10 +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"]); - - if (or_next_if_better) - goto next_match_rule; + 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()) @@ -1070,14 +1131,14 @@ struct MemoryBramPass : public Pass { log("rules. A block ram description looks like this:\n"); log("\n"); log(" bram RAMB1024X32 # name of BRAM cell\n"); - // log(" init 1 # set to '1' if BRAM can be initialized\n"); + log(" init 1 # set to '1' if BRAM can be initialized\n"); log(" abits 10 # number of address bits\n"); log(" dbits 32 # number of data bits\n"); log(" groups 2 # number of port groups\n"); log(" ports 1 1 # number of ports in each group\n"); log(" wrmode 1 0 # set to '1' if this groups is write ports\n"); - log(" enable 4 0 # number of enable bits (for write ports)\n"); - log(" transp 0 2 # transparatent (for read ports)\n"); + log(" enable 4 1 # number of enable bits\n"); + log(" transp 0 2 # transparent (for read ports)\n"); log(" clocks 1 2 # clock configuration\n"); log(" clkpol 2 2 # clock polarity configuration\n"); log(" endbram\n"); @@ -1095,7 +1156,7 @@ struct MemoryBramPass : public Pass { log("greater than 1 share the same configuration bit.\n"); log("\n"); log("Using the same bram name in different bram blocks will create different variants\n"); - log("of the bram. Verilog configration parameters for the bram are created as needed.\n"); + log("of the bram. Verilog configuration parameters for the bram are created as needed.\n"); log("\n"); log("It is also possible to create variants by repeating statements in the bram block\n"); log("and appending '@<label>' to the individual statements.\n"); @@ -1128,7 +1189,7 @@ struct MemoryBramPass : public Pass { log(" dcells ....... number of cells in 'data-direction'\n"); log(" cells ........ total number of cells (acells*dcells*dups)\n"); log("\n"); - log("The interface for the created bram instances is dervived from the bram\n"); + log("The interface for the created bram instances is derived from the bram\n"); log("description. Use 'techmap' to convert the created bram instances into\n"); log("instances of the actual bram cells of your target architecture.\n"); log("\n"); @@ -1139,6 +1200,9 @@ struct MemoryBramPass : public Pass { log("A match containing the command 'make_transp' will add external circuitry\n"); log("to simulate 'transparent read', if necessary.\n"); log("\n"); + log("A match containing the command 'make_outreg' will add external flip-flops\n"); + log("to implement synchronous read ports, if necessary.\n"); + log("\n"); log("A match containing the command 'shuffle_enable A' will re-organize\n"); log("the data bits to accommodate the enable pattern of port A.\n"); log("\n"); @@ -1147,7 +1211,7 @@ struct MemoryBramPass : public Pass { { rules_t rules; - log_header("Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n"); + log_header(design, "Executing MEMORY_BRAM pass (mapping $mem cells to block memories).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc index ccc196202..e068ef905 100644 --- a/passes/memory/memory_collect.cc +++ b/passes/memory/memory_collect.cc @@ -2,11 +2,11 @@ * 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 @@ -17,16 +17,13 @@ * */ -#include "kernel/register.h" -#include "kernel/log.h" -#include <sstream> -#include <algorithm> -#include <stdlib.h> +#include "kernel/yosys.h" +#include "kernel/sigtools.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b) +bool memcells_cmp(Cell *a, Cell *b) { if (a->type == "$memrd" && b->type == "$memrd") return a->name < b->name; @@ -35,108 +32,152 @@ bool memcells_cmp(RTLIL::Cell *a, RTLIL::Cell *b) return a->parameters.at("\\PRIORITY").as_int() < b->parameters.at("\\PRIORITY").as_int(); } -void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) +Cell *handle_memory(Module *module, RTLIL::Memory *memory) { - log("Collecting $memrd and $memwr for memory `%s' in module `%s':\n", + log("Collecting $memrd, $memwr and $meminit for memory `%s' in module `%s':\n", memory->name.c_str(), module->name.c_str()); int addr_bits = 0; - while ((1 << addr_bits) < memory->size) - addr_bits++; + + Const init_data(State::Sx, memory->size * memory->width); + SigMap sigmap(module); int wr_ports = 0; - RTLIL::SigSpec sig_wr_clk; - RTLIL::SigSpec sig_wr_clk_enable; - RTLIL::SigSpec sig_wr_clk_polarity; - RTLIL::SigSpec sig_wr_addr; - RTLIL::SigSpec sig_wr_data; - RTLIL::SigSpec sig_wr_en; + SigSpec sig_wr_clk; + SigSpec sig_wr_clk_enable; + SigSpec sig_wr_clk_polarity; + SigSpec sig_wr_addr; + SigSpec sig_wr_data; + SigSpec sig_wr_en; int rd_ports = 0; - RTLIL::SigSpec sig_rd_clk; - RTLIL::SigSpec sig_rd_clk_enable; - RTLIL::SigSpec sig_rd_clk_polarity; - RTLIL::SigSpec sig_rd_transparent; - RTLIL::SigSpec sig_rd_addr; - RTLIL::SigSpec sig_rd_data; + SigSpec sig_rd_clk; + SigSpec sig_rd_clk_enable; + SigSpec sig_rd_clk_polarity; + SigSpec sig_rd_transparent; + SigSpec sig_rd_addr; + SigSpec sig_rd_data; + SigSpec sig_rd_en; - std::vector<RTLIL::Cell*> del_cells; - std::vector<RTLIL::Cell*> memcells; + std::vector<Cell*> memcells; for (auto &cell_it : module->cells_) { - RTLIL::Cell *cell = cell_it.second; - if ((cell->type == "$memwr" || cell->type == "$memrd") && memory->name == cell->parameters["\\MEMID"].decode_string()) + Cell *cell = cell_it.second; + if (cell->type.in("$memrd", "$memwr", "$meminit") && memory->name == cell->parameters["\\MEMID"].decode_string()) { + addr_bits = max(addr_bits, cell->getParam("\\ABITS").as_int()); memcells.push_back(cell); + } + } + + if (memcells.empty()) { + log(" no cells found. removing memory.\n"); + return nullptr; } std::sort(memcells.begin(), memcells.end(), memcells_cmp); for (auto cell : memcells) { - if (cell->type == "$memwr" && memory->name == cell->parameters["\\MEMID"].decode_string()) + log(" %s (%s)\n", log_id(cell), log_id(cell->type)); + + if (cell->type == "$meminit") { - wr_ports++; - del_cells.push_back(cell); - - RTLIL::SigSpec clk = cell->getPort("\\CLK"); - RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]); - RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]); - RTLIL::SigSpec addr = cell->getPort("\\ADDR"); - RTLIL::SigSpec data = cell->getPort("\\DATA"); - RTLIL::SigSpec en = cell->getPort("\\EN"); - - clk.extend_u0(1, false); - clk_enable.extend_u0(1, false); - clk_polarity.extend_u0(1, false); - addr.extend_u0(addr_bits, false); - data.extend_u0(memory->width, false); - en.extend_u0(memory->width, false); - - sig_wr_clk.append(clk); - sig_wr_clk_enable.append(clk_enable); - sig_wr_clk_polarity.append(clk_polarity); - sig_wr_addr.append(addr); - sig_wr_data.append(data); - sig_wr_en.append(en); + SigSpec addr = sigmap(cell->getPort("\\ADDR")); + SigSpec data = sigmap(cell->getPort("\\DATA")); + + if (!addr.is_fully_const()) + log_error("Non-constant address %s in memory initialization %s.\n", log_signal(addr), log_id(cell)); + if (!data.is_fully_const()) + log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell)); + + int offset = (addr.as_int() - memory->start_offset) * memory->width; + + if (offset < 0 || offset + GetSize(data) > GetSize(init_data)) + log_warning("Address %s in memory initialization %s is out-of-bounds.\n", log_signal(addr), log_id(cell)); + + for (int i = 0; i < GetSize(data); i++) + if (0 <= i+offset && i+offset < GetSize(init_data)) + init_data.bits[i+offset] = data[i].data; + + continue; } - if (cell->type == "$memrd" && memory->name == cell->parameters["\\MEMID"].decode_string()) + if (cell->type == "$memwr") { - rd_ports++; - del_cells.push_back(cell); - - RTLIL::SigSpec clk = cell->getPort("\\CLK"); - RTLIL::SigSpec clk_enable = RTLIL::SigSpec(cell->parameters["\\CLK_ENABLE"]); - RTLIL::SigSpec clk_polarity = RTLIL::SigSpec(cell->parameters["\\CLK_POLARITY"]); - RTLIL::SigSpec transparent = RTLIL::SigSpec(cell->parameters["\\TRANSPARENT"]); - RTLIL::SigSpec addr = cell->getPort("\\ADDR"); - RTLIL::SigSpec data = cell->getPort("\\DATA"); - - clk.extend_u0(1, false); - clk_enable.extend_u0(1, false); - clk_polarity.extend_u0(1, false); - transparent.extend_u0(1, false); - addr.extend_u0(addr_bits, false); - data.extend_u0(memory->width, false); - - sig_rd_clk.append(clk); - sig_rd_clk_enable.append(clk_enable); - sig_rd_clk_polarity.append(clk_polarity); - sig_rd_transparent.append(transparent); - sig_rd_addr.append(addr); - sig_rd_data.append(data); + SigSpec clk = sigmap(cell->getPort("\\CLK")); + SigSpec clk_enable = SigSpec(cell->parameters["\\CLK_ENABLE"]); + SigSpec clk_polarity = SigSpec(cell->parameters["\\CLK_POLARITY"]); + SigSpec addr = sigmap(cell->getPort("\\ADDR")); + SigSpec data = sigmap(cell->getPort("\\DATA")); + SigSpec en = sigmap(cell->getPort("\\EN")); + + if (!en.is_fully_zero()) + { + clk.extend_u0(1, false); + clk_enable.extend_u0(1, false); + clk_polarity.extend_u0(1, false); + addr.extend_u0(addr_bits, false); + data.extend_u0(memory->width, false); + en.extend_u0(memory->width, false); + + sig_wr_clk.append(clk); + sig_wr_clk_enable.append(clk_enable); + sig_wr_clk_polarity.append(clk_polarity); + sig_wr_addr.append(addr); + sig_wr_data.append(data); + sig_wr_en.append(en); + + wr_ports++; + } + continue; + } + + if (cell->type == "$memrd") + { + SigSpec clk = sigmap(cell->getPort("\\CLK")); + SigSpec clk_enable = SigSpec(cell->parameters["\\CLK_ENABLE"]); + SigSpec clk_polarity = SigSpec(cell->parameters["\\CLK_POLARITY"]); + SigSpec transparent = SigSpec(cell->parameters["\\TRANSPARENT"]); + SigSpec addr = sigmap(cell->getPort("\\ADDR")); + SigSpec data = sigmap(cell->getPort("\\DATA")); + SigSpec en = sigmap(cell->getPort("\\EN")); + + if (!en.is_fully_zero()) + { + clk.extend_u0(1, false); + clk_enable.extend_u0(1, false); + clk_polarity.extend_u0(1, false); + transparent.extend_u0(1, false); + addr.extend_u0(addr_bits, false); + data.extend_u0(memory->width, false); + + sig_rd_clk.append(clk); + sig_rd_clk_enable.append(clk_enable); + sig_rd_clk_polarity.append(clk_polarity); + sig_rd_transparent.append(transparent); + sig_rd_addr.append(addr); + sig_rd_data.append(data); + sig_rd_en.append(en); + + rd_ports++; + } + continue; } } std::stringstream sstr; sstr << "$mem$" << memory->name.str() << "$" << (autoidx++); - RTLIL::Cell *mem = module->addCell(sstr.str(), "$mem"); - mem->parameters["\\MEMID"] = RTLIL::Const(memory->name.str()); - mem->parameters["\\WIDTH"] = RTLIL::Const(memory->width); - mem->parameters["\\OFFSET"] = RTLIL::Const(memory->start_offset); - mem->parameters["\\SIZE"] = RTLIL::Const(memory->size); - mem->parameters["\\ABITS"] = RTLIL::Const(addr_bits); + Cell *mem = module->addCell(sstr.str(), "$mem"); + mem->parameters["\\MEMID"] = Const(memory->name.str()); + mem->parameters["\\WIDTH"] = Const(memory->width); + mem->parameters["\\OFFSET"] = Const(memory->start_offset); + mem->parameters["\\SIZE"] = Const(memory->size); + mem->parameters["\\ABITS"] = Const(addr_bits); + + while (GetSize(init_data) > 1 && init_data.bits.back() == State::Sx && init_data.bits[GetSize(init_data)-2] == State::Sx) + init_data.bits.pop_back(); + mem->parameters["\\INIT"] = init_data; log_assert(sig_wr_clk.size() == wr_ports); log_assert(sig_wr_clk_enable.size() == wr_ports && sig_wr_clk_enable.is_fully_const()); @@ -145,9 +186,9 @@ void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) log_assert(sig_wr_data.size() == wr_ports * memory->width); log_assert(sig_wr_en.size() == wr_ports * memory->width); - mem->parameters["\\WR_PORTS"] = RTLIL::Const(wr_ports); - mem->parameters["\\WR_CLK_ENABLE"] = wr_ports ? sig_wr_clk_enable.as_const() : RTLIL::Const(0, 0); - mem->parameters["\\WR_CLK_POLARITY"] = wr_ports ? sig_wr_clk_polarity.as_const() : RTLIL::Const(0, 0); + mem->parameters["\\WR_PORTS"] = Const(wr_ports); + mem->parameters["\\WR_CLK_ENABLE"] = wr_ports ? sig_wr_clk_enable.as_const() : Const(0, 1); + mem->parameters["\\WR_CLK_POLARITY"] = wr_ports ? sig_wr_clk_polarity.as_const() : Const(0, 1); mem->setPort("\\WR_CLK", sig_wr_clk); mem->setPort("\\WR_ADDR", sig_wr_addr); @@ -160,30 +201,36 @@ void handle_memory(RTLIL::Module *module, RTLIL::Memory *memory) log_assert(sig_rd_addr.size() == rd_ports * addr_bits); log_assert(sig_rd_data.size() == rd_ports * memory->width); - mem->parameters["\\RD_PORTS"] = RTLIL::Const(rd_ports); - mem->parameters["\\RD_CLK_ENABLE"] = rd_ports ? sig_rd_clk_enable.as_const() : RTLIL::Const(0, 0); - mem->parameters["\\RD_CLK_POLARITY"] = rd_ports ? sig_rd_clk_polarity.as_const() : RTLIL::Const(0, 0); - mem->parameters["\\RD_TRANSPARENT"] = rd_ports ? sig_rd_transparent.as_const() : RTLIL::Const(0, 0); + mem->parameters["\\RD_PORTS"] = Const(rd_ports); + mem->parameters["\\RD_CLK_ENABLE"] = rd_ports ? sig_rd_clk_enable.as_const() : Const(0, 1); + mem->parameters["\\RD_CLK_POLARITY"] = rd_ports ? sig_rd_clk_polarity.as_const() : Const(0, 1); + mem->parameters["\\RD_TRANSPARENT"] = rd_ports ? sig_rd_transparent.as_const() : Const(0, 1); mem->setPort("\\RD_CLK", sig_rd_clk); mem->setPort("\\RD_ADDR", sig_rd_addr); mem->setPort("\\RD_DATA", sig_rd_data); + mem->setPort("\\RD_EN", sig_rd_en); - for (auto c : del_cells) + for (auto c : memcells) module->remove(c); + + return mem; } -static void handle_module(RTLIL::Design *design, RTLIL::Module *module) +static void handle_module(Design *design, Module *module) { - std::vector<RTLIL::IdString> delme; + std::vector<pair<Cell*, IdString>> finqueue; + for (auto &mem_it : module->memories) if (design->selected(module, mem_it.second)) { - handle_memory(module, mem_it.second); - delme.push_back(mem_it.first); + Cell *c = handle_memory(module, mem_it.second); + finqueue.push_back(pair<Cell*, IdString>(c, mem_it.first)); } - for (auto &it : delme) { - delete module->memories.at(it); - module->memories.erase(it); + for (auto &it : finqueue) { + delete module->memories.at(it.second); + module->memories.erase(it.second); + if (it.first) + module->rename(it.first, it.second); } } @@ -200,12 +247,12 @@ struct MemoryCollectPass : public Pass { log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing MEMORY_COLLECT pass (generating $mem cells).\n"); + log_header(design, "Executing MEMORY_COLLECT pass (generating $mem cells).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) handle_module(design, mod_it.second); } } MemoryCollectPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc index d3cc681a2..40691d160 100644 --- a/passes/memory/memory_dff.cc +++ b/passes/memory/memory_dff.cc @@ -2,11 +2,11 @@ * 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 @@ -17,170 +17,251 @@ * */ -#include "kernel/register.h" -#include "kernel/log.h" -#include <stdlib.h> -#include <sstream> +#include "kernel/yosys.h" +#include "kernel/sigtools.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -void normalize_sig(RTLIL::Module *module, RTLIL::SigSpec &sig) +struct MemoryDffWorker { - for (auto &conn : module->connections()) - sig.replace(conn.first, conn.second); -} + Module *module; + SigMap sigmap; -bool find_sig_before_dff(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false) -{ - normalize_sig(module, sig); + vector<Cell*> dff_cells; + 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) { } - for (auto &bit : sig) + bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false) { - if (bit.wire == NULL) - continue; + sigmap.apply(sig); - for (auto cell : dff_cells) + for (auto &bit : sig) { - if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) { - if (cell->getPort("\\CLK") != clk) - continue; - if (cell->parameters["\\CLK_POLARITY"].as_bool() != clk_polarity) - continue; - } - - RTLIL::SigSpec q_norm = cell->getPort(after ? "\\D" : "\\Q"); - normalize_sig(module, q_norm); - - RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(after ? "\\Q" : "\\D")); - if (d.size() != 1) + if (bit.wire == NULL) continue; - bit = d; - clk = cell->getPort("\\CLK"); - clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool(); - goto replaced_this_bit; - } + for (auto cell : dff_cells) + { + if (after && forward_merged_dffs.count(cell)) + continue; - return false; - replaced_this_bit:; - } + SigSpec this_clk = cell->getPort("\\CLK"); + bool this_clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool(); - return true; -} + if (invbits.count(this_clk)) { + this_clk = invbits.at(this_clk); + this_clk_polarity = !this_clk_polarity; + } -void handle_wr_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::Cell *cell) -{ - log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str()); + if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) { + if (this_clk != clk) + continue; + if (this_clk_polarity != clk_polarity) + continue; + } - RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx); - bool clk_polarity = 0; + RTLIL::SigSpec q_norm = cell->getPort(after ? "\\D" : "\\Q"); + sigmap.apply(q_norm); - RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR"); - if (!find_sig_before_dff(module, dff_cells, sig_addr, clk, clk_polarity)) { - log("no (compatible) $dff for address input found.\n"); - return; - } + RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(after ? "\\Q" : "\\D")); + if (d.size() != 1) + continue; - RTLIL::SigSpec sig_data = cell->getPort("\\DATA"); - if (!find_sig_before_dff(module, dff_cells, sig_data, clk, clk_polarity)) { - log("no (compatible) $dff for data input found.\n"); - return; - } + bit = d; + clk = this_clk; + clk_polarity = this_clk_polarity; + candidate_dffs.insert(cell); + goto replaced_this_bit; + } - RTLIL::SigSpec sig_en = cell->getPort("\\EN"); - if (!find_sig_before_dff(module, dff_cells, sig_en, clk, clk_polarity)) { - log("no (compatible) $dff for enable input found.\n"); - return; - } + return false; + replaced_this_bit:; + } - if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) { - 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; + return true; } - log("no (compatible) $dff found.\n"); -} + void handle_wr_cell(RTLIL::Cell *cell) + { + log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str()); -void disconnect_dff(RTLIL::Module *module, RTLIL::SigSpec sig) -{ - normalize_sig(module, sig); - sig.sort_and_unify(); + RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx); + bool clk_polarity = 0; + candidate_dffs.clear(); - std::stringstream sstr; - sstr << "$memory_dff_disconnected$" << (autoidx++); + RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR"); + if (!find_sig_before_dff(sig_addr, clk, clk_polarity)) { + log("no (compatible) $dff for address input found.\n"); + return; + } - RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size()); + RTLIL::SigSpec sig_data = cell->getPort("\\DATA"); + if (!find_sig_before_dff(sig_data, clk, clk_polarity)) { + log("no (compatible) $dff for data input found.\n"); + return; + } - for (auto cell : module->cells()) - if (cell->type == "$dff") { - RTLIL::SigSpec new_q = cell->getPort("\\Q"); - new_q.replace(sig, new_sig); - cell->setPort("\\Q", new_q); + RTLIL::SigSpec sig_en = cell->getPort("\\EN"); + if (!find_sig_before_dff(sig_en, clk, clk_polarity)) { + log("no (compatible) $dff for enable input found.\n"); + return; } -} -void handle_rd_cell(RTLIL::Module *module, std::vector<RTLIL::Cell*> &dff_cells, RTLIL::Cell *cell) -{ - log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str()); + 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; + } - bool clk_polarity = 0; + log("no (compatible) $dff found.\n"); + } - RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx); - RTLIL::SigSpec sig_data = cell->getPort("\\DATA"); - if (find_sig_before_dff(module, dff_cells, sig_data, clk_data, clk_polarity, true) && - clk_data != RTLIL::SigSpec(RTLIL::State::Sx)) + void disconnect_dff(RTLIL::SigSpec sig) { - disconnect_dff(module, sig_data); - cell->setPort("\\CLK", clk_data); - cell->setPort("\\DATA", sig_data); - cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); - cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); - cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0); - log("merged data $dff to cell.\n"); - return; + sigmap.apply(sig); + sig.sort_and_unify(); + + std::stringstream sstr; + sstr << "$memory_dff_disconnected$" << (autoidx++); + + RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size()); + + for (auto cell : module->cells()) + if (cell->type == "$dff") { + RTLIL::SigSpec new_q = cell->getPort("\\Q"); + new_q.replace(sig, new_sig); + cell->setPort("\\Q", new_q); + } } - RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx); - RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR"); - if (find_sig_before_dff(module, dff_cells, sig_addr, clk_addr, clk_polarity) && - clk_addr != RTLIL::SigSpec(RTLIL::State::Sx)) + void handle_rd_cell(RTLIL::Cell *cell) { - cell->setPort("\\CLK", clk_addr); - cell->setPort("\\ADDR", sig_addr); - cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); - cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); - cell->parameters["\\TRANSPARENT"] = RTLIL::Const(1); - log("merged address $dff to cell.\n"); - return; - } + log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str()); - log("no (compatible) $dff found.\n"); -} + bool clk_polarity = 0; -void handle_module(RTLIL::Module *module, bool flag_wr_only) -{ - std::vector<RTLIL::Cell*> dff_cells; + RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx); + RTLIL::SigSpec sig_data = cell->getPort("\\DATA"); + + for (auto bit : sigmap(sig_data)) + if (sigbit_users_count[bit] > 1) + goto skip_ff_after_read_merging; - for (auto cell : module->cells()) - if (cell->type == "$dff") - dff_cells.push_back(cell); + if (mux_cells_a.count(sig_data) || mux_cells_b.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")); + + sig_data = sigmap(mux->getPort("\\Y")); + for (auto bit : sig_data) + if (sigbit_users_count[bit] > 1) + goto skip_ff_after_read_merging; + + if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && sig_data == check_q) + { + disconnect_dff(sig_data); + cell->setPort("\\CLK", clk_data); + cell->setPort("\\EN", enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S")); + cell->setPort("\\DATA", sig_data); + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); + cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0); + log("merged data $dff with rd enable to cell.\n"); + return; + } + } + else + { + if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx)) + { + disconnect_dff(sig_data); + cell->setPort("\\CLK", clk_data); + cell->setPort("\\EN", State::S1); + cell->setPort("\\DATA", sig_data); + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); + cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0); + log("merged data $dff to cell.\n"); + return; + } + } - for (auto cell : module->selected_cells()) - if (cell->type == "$memwr" && !cell->parameters["\\CLK_ENABLE"].as_bool()) - handle_wr_cell(module, dff_cells, cell); + skip_ff_after_read_merging:; + RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx); + RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR"); + if (find_sig_before_dff(sig_addr, clk_addr, clk_polarity) && + clk_addr != RTLIL::SigSpec(RTLIL::State::Sx)) + { + cell->setPort("\\CLK", clk_addr); + cell->setPort("\\EN", State::S1); + cell->setPort("\\ADDR", sig_addr); + cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1); + cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity); + cell->parameters["\\TRANSPARENT"] = RTLIL::Const(1); + log("merged address $dff to cell.\n"); + return; + } + + log("no (compatible) $dff found.\n"); + } + + void run(bool flag_wr_only) + { + for (auto wire : module->wires()) { + if (wire->port_output) + for (auto bit : sigmap(wire)) + sigbit_users_count[bit]++; + } + + for (auto cell : module->cells()) { + if (cell->type == "$dff") + dff_cells.push_back(cell); + if (cell->type == "$mux") { + mux_cells_a[sigmap(cell->getPort("\\A"))] = cell; + mux_cells_b[sigmap(cell->getPort("\\B"))] = cell; + } + if (cell->type == "$not" || cell->type == "$_NOT_" || (cell->type == "$logic_not" && GetSize(cell->getPort("\\A")) == 1)) { + SigSpec sig_a = cell->getPort("\\A"); + SigSpec sig_y = cell->getPort("\\Y"); + if (cell->type == "$not") + sig_a.extend_u0(GetSize(sig_y), cell->getParam("\\A_SIGNED").as_bool()); + if (cell->type == "$logic_not") + sig_y.extend_u0(1); + for (int i = 0; i < GetSize(sig_y); i++) + invbits[sig_y[i]] = sig_a[i]; + } + for (auto &conn : cell->connections()) + if (!cell->known() || cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + sigbit_users_count[bit]++; + } - if (!flag_wr_only) for (auto cell : module->selected_cells()) - if (cell->type == "$memrd" && !cell->parameters["\\CLK_ENABLE"].as_bool()) - handle_rd_cell(module, dff_cells, cell); -} + if (cell->type == "$memwr" && !cell->parameters["\\CLK_ENABLE"].as_bool()) + handle_wr_cell(cell); + + if (!flag_wr_only) + for (auto cell : module->selected_cells()) + if (cell->type == "$memrd" && !cell->parameters["\\CLK_ENABLE"].as_bool()) + handle_rd_cell(cell); + } +}; struct MemoryDffPass : public Pass { MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { } @@ -194,7 +275,7 @@ struct MemoryDffPass : public Pass { log("I.e. it consumes an asynchronous memory port and the flip-flops at its\n"); log("interface and yields a synchronous memory port.\n"); log("\n"); - log(" -wr_only\n"); + log(" -nordfff\n"); log(" do not merge registers on read ports\n"); log("\n"); } @@ -202,11 +283,11 @@ struct MemoryDffPass : public Pass { { bool flag_wr_only = false; - log_header("Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n"); + log_header(design, "Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-wr_only") { + if (args[argidx] == "-nordff" || args[argidx] == "-wr_only") { flag_wr_only = true; continue; } @@ -214,9 +295,11 @@ struct MemoryDffPass : public Pass { } extra_args(args, argidx, design); - for (auto mod : design->selected_modules()) - handle_module(mod, flag_wr_only); + for (auto mod : design->selected_modules()) { + MemoryDffWorker worker(mod); + worker.run(flag_wr_only); + } } } MemoryDffPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_map.cc b/passes/memory/memory_map.cc index 4fb10a989..bffeec857 100644 --- a/passes/memory/memory_map.cc +++ b/passes/memory/memory_map.cc @@ -2,11 +2,11 @@ * 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 @@ -37,7 +37,7 @@ struct MemoryMapWorker { std::stringstream sstr; sstr << "$memory" << name.str() << token1; - + if (i >= 0) sstr << "[" << i << "]"; @@ -80,13 +80,20 @@ struct MemoryMapWorker { std::set<int> static_ports; std::map<int, RTLIL::SigSpec> static_cells_map; + + int wr_ports = cell->parameters["\\WR_PORTS"].as_int(); + int rd_ports = cell->parameters["\\RD_PORTS"].as_int(); + int mem_size = cell->parameters["\\SIZE"].as_int(); int mem_width = cell->parameters["\\WIDTH"].as_int(); int mem_offset = cell->parameters["\\OFFSET"].as_int(); int mem_abits = cell->parameters["\\ABITS"].as_int(); + SigSpec init_data = cell->getParam("\\INIT"); + init_data.extend_u0(mem_size*mem_width, true); + // delete unused memory cell - if (cell->parameters["\\RD_PORTS"].as_int() == 0 && cell->parameters["\\WR_PORTS"].as_int() == 0) { + if (wr_ports == 0 && rd_ports == 0) { module->remove(cell); return; } @@ -95,6 +102,8 @@ struct MemoryMapWorker RTLIL::SigSpec clocks = cell->getPort("\\WR_CLK"); RTLIL::Const clocks_pol = cell->parameters["\\WR_CLK_POLARITY"]; RTLIL::Const clocks_en = cell->parameters["\\WR_CLK_ENABLE"]; + clocks_pol.bits.resize(wr_ports); + clocks_en.bits.resize(wr_ports); RTLIL::SigSpec refclock; RTLIL::State refclock_pol = RTLIL::State::Sx; for (int i = 0; i < clocks.size(); i++) { @@ -110,7 +119,7 @@ struct MemoryMapWorker // FIXME: Actually we should check for wr_en.is_fully_const() also and // create a $adff cell with this ports wr_en input as reset pin when wr_en // is not a simple static 1. - static_cells_map[wr_addr.as_int()] = wr_data; + static_cells_map[wr_addr.as_int() - mem_offset] = wr_data; static_ports.insert(i); continue; } @@ -165,7 +174,10 @@ struct MemoryMapWorker w_out_name = genid(cell->name, "", i, "$q"); RTLIL::Wire *w_out = module->addWire(w_out_name, mem_width); - w_out->start_offset = mem_offset; + SigSpec w_init = init_data.extract(i*mem_width, mem_width); + + if (!w_init.is_fully_undef()) + w_out->attributes["\\init"] = w_init.as_const(); data_reg_out.push_back(RTLIL::SigSpec(w_out)); c->setPort("\\Q", data_reg_out.back()); @@ -180,39 +192,51 @@ struct MemoryMapWorker { RTLIL::SigSpec rd_addr = cell->getPort("\\RD_ADDR").extract(i*mem_abits, mem_abits); + if (mem_offset) + rd_addr = module->Sub(NEW_ID, rd_addr, SigSpec(mem_offset, GetSize(rd_addr))); + std::vector<RTLIL::SigSpec> rd_signals; rd_signals.push_back(cell->getPort("\\RD_DATA").extract(i*mem_width, mem_width)); if (cell->parameters["\\RD_CLK_ENABLE"].bits[i] == RTLIL::State::S1) { + RTLIL::Cell *dff_cell = nullptr; + if (cell->parameters["\\RD_TRANSPARENT"].bits[i] == RTLIL::State::S1) { - RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdreg", i), "$dff"); - c->parameters["\\WIDTH"] = RTLIL::Const(mem_abits); - c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); - c->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1)); - c->setPort("\\D", rd_addr); + dff_cell = module->addCell(genid(cell->name, "$rdreg", i), "$dff"); + dff_cell->parameters["\\WIDTH"] = RTLIL::Const(mem_abits); + dff_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); + dff_cell->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1)); + dff_cell->setPort("\\D", rd_addr); count_dff++; RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$q"), mem_abits); - c->setPort("\\Q", RTLIL::SigSpec(w)); + dff_cell->setPort("\\Q", RTLIL::SigSpec(w)); rd_addr = RTLIL::SigSpec(w); } else { - RTLIL::Cell *c = module->addCell(genid(cell->name, "$rdreg", i), "$dff"); - c->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; - c->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); - c->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1)); - c->setPort("\\Q", rd_signals.back()); + dff_cell = module->addCell(genid(cell->name, "$rdreg", i), "$dff"); + dff_cell->parameters["\\WIDTH"] = cell->parameters["\\WIDTH"]; + dff_cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(cell->parameters["\\RD_CLK_POLARITY"].bits[i]); + dff_cell->setPort("\\CLK", cell->getPort("\\RD_CLK").extract(i, 1)); + dff_cell->setPort("\\Q", rd_signals.back()); count_dff++; RTLIL::Wire *w = module->addWire(genid(cell->name, "$rdreg", i, "$d"), mem_width); rd_signals.clear(); rd_signals.push_back(RTLIL::SigSpec(w)); - c->setPort("\\D", rd_signals.back()); + dff_cell->setPort("\\D", rd_signals.back()); + } + + SigBit en_bit = cell->getPort("\\RD_EN").extract(i); + if (en_bit != State::S1) { + SigSpec new_d = module->Mux(genid(cell->name, "$rdenmux", i), + dff_cell->getPort("\\Q"), dff_cell->getPort("\\D"), en_bit); + dff_cell->setPort("\\D", new_d); } } @@ -256,6 +280,10 @@ struct MemoryMapWorker RTLIL::SigSpec wr_addr = cell->getPort("\\WR_ADDR").extract(j*mem_abits, mem_abits); RTLIL::SigSpec wr_data = cell->getPort("\\WR_DATA").extract(j*mem_width, mem_width); RTLIL::SigSpec wr_en = cell->getPort("\\WR_EN").extract(j*mem_width, mem_width); + + if (mem_offset) + wr_addr = module->Sub(NEW_ID, wr_addr, SigSpec(mem_offset, GetSize(wr_addr))); + RTLIL::Wire *w_seladdr = addr_decode(wr_addr, RTLIL::SigSpec(i, mem_abits)); int wr_offset = 0; @@ -335,11 +363,11 @@ struct MemoryMapPass : public Pass { log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n"); + log_header(design, "Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n"); extra_args(args, 1, design); for (auto mod : design->selected_modules()) MemoryMapWorker(design, mod); } } MemoryMapPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/memory/memory_share.cc b/passes/memory/memory_share.cc index a2f89f6d9..f298169de 100644 --- a/passes/memory/memory_share.cc +++ b/passes/memory/memory_share.cc @@ -2,11 +2,11 @@ * 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 @@ -43,7 +43,7 @@ struct MemoryShareWorker CellTypes cone_ct; std::map<RTLIL::SigBit, std::pair<RTLIL::Cell*, int>> sig_to_mux; - std::map<std::set<std::map<RTLIL::SigBit, bool>>, RTLIL::SigBit> conditions_logic_cache; + std::map<pair<std::set<std::map<SigBit, bool>>, SigBit>, SigBit> conditions_logic_cache; // ----------------------------------------------------------------- @@ -79,7 +79,7 @@ struct MemoryShareWorker } return false; } - + for (int i = 0; i < int(sig_s.size()); i++) { @@ -109,10 +109,12 @@ struct MemoryShareWorker return false; } - RTLIL::SigBit conditions_to_logic(std::set<std::map<RTLIL::SigBit, bool>> &conditions, int &created_conditions) + RTLIL::SigBit conditions_to_logic(std::set<std::map<RTLIL::SigBit, bool>> &conditions, SigBit olden, int &created_conditions) { - if (conditions_logic_cache.count(conditions)) - return conditions_logic_cache.at(conditions); + auto key = make_pair(conditions, olden); + + if (conditions_logic_cache.count(key)) + return conditions_logic_cache.at(key); RTLIL::SigSpec terms; for (auto &cond : conditions) { @@ -125,10 +127,16 @@ struct MemoryShareWorker created_conditions++; } - if (terms.size() > 1) + if (olden.wire != nullptr || olden != State::S1) + terms.append(olden); + + if (GetSize(terms) == 0) + terms = State::S1; + + if (GetSize(terms) > 1) terms = module->ReduceAnd(NEW_ID, terms); - return conditions_logic_cache[conditions] = terms; + return conditions_logic_cache[key] = terms; } void translate_rd_feedback_to_en(std::string memid, std::vector<RTLIL::Cell*> &rd_ports, std::vector<RTLIL::Cell*> &wr_ports) @@ -137,15 +145,14 @@ struct MemoryShareWorker std::map<RTLIL::SigBit, std::set<RTLIL::SigBit>> muxtree_upstream_map; std::set<RTLIL::SigBit> non_feedback_nets; - for (auto wire_it : module->wires_) - if (wire_it.second->port_output) { - std::vector<RTLIL::SigBit> bits = RTLIL::SigSpec(wire_it.second); + for (auto wire : module->wires()) + if (wire->port_output) { + std::vector<RTLIL::SigBit> bits = sigmap(wire); non_feedback_nets.insert(bits.begin(), bits.end()); } - for (auto cell_it : module->cells_) + for (auto cell : module->cells()) { - RTLIL::Cell *cell = cell_it.second; bool ignore_data_port = false; if (cell->type == "$mux" || cell->type == "$pmux") @@ -170,7 +177,7 @@ struct MemoryShareWorker cell->parameters.at("\\MEMID").decode_string() == memid) ignore_data_port = true; - for (auto conn : cell_it.second->connections()) + for (auto conn : cell->connections()) { if (ignore_data_port && conn.first == "\\DATA") continue; @@ -207,7 +214,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]); @@ -237,13 +244,8 @@ struct MemoryShareWorker std::map<RTLIL::SigBit, bool> state; std::set<std::map<RTLIL::SigBit, bool>> conditions; - if (cell_en[i].wire != NULL) { - state[cell_en[i]] = false; - conditions.insert(state); - } - find_data_feedback(async_rd_bits.at(sig_addr).at(i), cell_data[i], state, conditions); - cell_en[i] = conditions_to_logic(conditions, created_conditions); + cell_en[i] = conditions_to_logic(conditions, cell_en[i], created_conditions); } if (created_conditions) { @@ -489,8 +491,8 @@ struct MemoryShareWorker if (wr_ports.size() <= 1) return; - ezDefaultSAT ez; - SatGen satgen(&ez, &modwalker.sigmap); + ezSatPtr ez; + SatGen satgen(ez.get(), &modwalker.sigmap); // find list of considered ports and port pairs @@ -553,7 +555,7 @@ struct MemoryShareWorker if (considered_port_pairs.count(i) || considered_port_pairs.count(i+1)) { RTLIL::SigSpec sig = modwalker.sigmap(wr_ports[i]->getPort("\\EN")); - port_to_sat_variable[i] = ez.expression(ez.OpOr, satgen.importSigSpec(sig)); + port_to_sat_variable[i] = ez->expression(ez->OpOr, satgen.importSigSpec(sig)); std::vector<RTLIL::SigBit> bits = sig; bits_queue.insert(bits.begin(), bits.end()); @@ -582,7 +584,7 @@ struct MemoryShareWorker vector<int> ez_wire_bits = satgen.importSigSpec(wire); for (int i : ez_wire_bits) for (int j : ez_wire_bits) - if (i != j) ez.assume(ez.NOT(i), j); + if (i != j) ez->assume(ez->NOT(i), j); } log(" Common input cone for all EN signals: %d cells.\n", int(sat_cells.size())); @@ -590,7 +592,7 @@ struct MemoryShareWorker for (auto cell : sat_cells) satgen.importCell(cell); - log(" Size of unconstrained SAT problem: %d variables, %d clauses\n", ez.numCnfVariables(), ez.numCnfClauses()); + log(" Size of unconstrained SAT problem: %d variables, %d clauses\n", ez->numCnfVariables(), ez->numCnfClauses()); // merge subsequent ports if possible @@ -599,13 +601,13 @@ struct MemoryShareWorker if (!considered_port_pairs.count(i)) continue; - if (ez.solve(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i))) { + if (ez->solve(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i))) { log(" According to SAT solver sharing of port %d with port %d is not possible.\n", i-1, i); continue; } log(" Merging port %d into port %d.\n", i-1, i); - port_to_sat_variable.at(i) = ez.OR(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i)); + port_to_sat_variable.at(i) = ez->OR(port_to_sat_variable.at(i-1), port_to_sat_variable.at(i)); RTLIL::SigSpec last_addr = wr_ports[i-1]->getPort("\\ADDR"); RTLIL::SigSpec last_data = wr_ports[i-1]->getPort("\\DATA"); @@ -663,10 +665,8 @@ struct MemoryShareWorker std::map<std::string, std::pair<std::vector<RTLIL::Cell*>, std::vector<RTLIL::Cell*>>> memindex; sigmap_xmux = sigmap; - for (auto &it : module->cells_) + for (auto cell : module->cells()) { - RTLIL::Cell *cell = it.second; - if (cell->type == "$memrd") memindex[cell->parameters.at("\\MEMID").decode_string()].first.push_back(cell); @@ -743,11 +743,11 @@ struct MemorySharePass : public Pass { log("\n"); log("Note that in addition to the algorithms implemented in this pass, the $memrd\n"); log("and $memwr cells are also subject to generic resource sharing passes (and other\n"); - log("optimizations) such as opt_share.\n"); + log("optimizations) such as \"share\" and \"opt_merge\".\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing MEMORY_SHARE pass (consolidating $memrc/$memwr cells).\n"); + log_header(design, "Executing MEMORY_SHARE pass (consolidating $memrc/$memwr cells).\n"); extra_args(args, 1, design); for (auto module : design->selected_modules()) MemoryShareWorker(design, module); diff --git a/passes/memory/memory_unpack.cc b/passes/memory/memory_unpack.cc index e650facb4..a0fc31b5e 100644 --- a/passes/memory/memory_unpack.cc +++ b/passes/memory/memory_unpack.cc @@ -2,11 +2,11 @@ * 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 @@ -57,6 +57,7 @@ void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory) cell->parameters["\\CLK_POLARITY"] = RTLIL::SigSpec(memory->parameters.at("\\RD_CLK_POLARITY")).extract(i, 1).as_const(); cell->parameters["\\TRANSPARENT"] = RTLIL::SigSpec(memory->parameters.at("\\RD_TRANSPARENT")).extract(i, 1).as_const(); cell->setPort("\\CLK", memory->getPort("\\RD_CLK").extract(i, 1)); + cell->setPort("\\EN", memory->getPort("\\RD_EN").extract(i, 1)); cell->setPort("\\ADDR", memory->getPort("\\RD_ADDR").extract(i*abits, abits)); cell->setPort("\\DATA", memory->getPort("\\RD_DATA").extract(i*mem->width, mem->width)); } @@ -76,6 +77,41 @@ void handle_memory(RTLIL::Module *module, RTLIL::Cell *memory) cell->setPort("\\DATA", memory->getPort("\\WR_DATA").extract(i*mem->width, mem->width)); } + Const initval = memory->parameters.at("\\INIT"); + RTLIL::Cell *last_init_cell = nullptr; + SigSpec last_init_data; + int last_init_addr=0; + + for (int i = 0; i < GetSize(initval) && i/mem->width < (1 << abits); i += mem->width) { + Const val = initval.extract(i, mem->width, State::Sx); + for (auto bit : val.bits) + if (bit != State::Sx) + goto found_non_undef_initval; + continue; + found_non_undef_initval: + if (last_init_cell && last_init_addr+1 == i/mem->width) { + last_init_cell->parameters["\\WORDS"] = last_init_cell->parameters["\\WORDS"].as_int() + 1; + last_init_data.append(val); + last_init_addr++; + } else { + if (last_init_cell) + last_init_cell->setPort("\\DATA", last_init_data); + RTLIL::Cell *cell = module->addCell(NEW_ID, "$meminit"); + cell->parameters["\\MEMID"] = mem_name.str(); + cell->parameters["\\ABITS"] = memory->parameters.at("\\ABITS"); + cell->parameters["\\WIDTH"] = memory->parameters.at("\\WIDTH"); + cell->parameters["\\WORDS"] = 1; + cell->parameters["\\PRIORITY"] = i/mem->width; + cell->setPort("\\ADDR", SigSpec(i/mem->width, abits)); + last_init_cell = cell; + last_init_addr = i/mem->width; + last_init_data = val; + } + } + + if (last_init_cell) + last_init_cell->setPort("\\DATA", last_init_data); + module->remove(memory); } @@ -102,12 +138,12 @@ struct MemoryUnpackPass : public Pass { log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing MEMORY_UNPACK pass (generating $memrd/$memwr cells form $mem cells).\n"); + log_header(design, "Executing MEMORY_UNPACK pass (generating $memrd/$memwr cells form $mem cells).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) if (design->selected(mod_it.second)) handle_module(design, mod_it.second); } } MemoryUnpackPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 6b075cd9a..a8b1537bb 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -1,11 +1,11 @@ OBJS += passes/opt/opt.o -OBJS += passes/opt/opt_share.o +OBJS += passes/opt/opt_merge.o OBJS += passes/opt/opt_muxtree.o OBJS += passes/opt/opt_reduce.o OBJS += passes/opt/opt_rmdff.o -OBJS += passes/opt/opt_clean.o -OBJS += passes/opt/opt_const.o +OBJS += passes/opt/opt_clean.o +OBJS += passes/opt/opt_expr.o ifneq ($(SMALL),1) OBJS += passes/opt/share.o diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc index 25419375e..13ea5469b 100644 --- a/passes/opt/opt.cc +++ b/passes/opt/opt.cc @@ -2,11 +2,11 @@ * 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 @@ -37,23 +37,23 @@ struct OptPass : public Pass { log("a series of trivial optimizations and cleanups. This pass executes the other\n"); log("passes in the following order:\n"); log("\n"); - log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc]\n"); - log(" opt_share -nomux\n"); + log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); + log(" opt_merge [-share_all] -nomux\n"); log("\n"); log(" do\n"); log(" opt_muxtree\n"); log(" opt_reduce [-fine] [-full]\n"); - log(" opt_share\n"); + log(" opt_merge [-share_all]\n"); log(" opt_rmdff\n"); log(" opt_clean [-purge]\n"); - log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc]\n"); + log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); log(" while <changed design>\n"); log("\n"); log("When called with -fast the following script is used instead:\n"); log("\n"); log(" do\n"); - log(" opt_const [-mux_undef] [-mux_bool] [-undriven] [-fine] [-full] [-keepdc]\n"); - log(" opt_share\n"); + log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n"); + log(" opt_merge [-share_all]\n"); log(" opt_rmdff\n"); log(" opt_clean [-purge]\n"); log(" while <changed design in opt_rmdff>\n"); @@ -66,11 +66,12 @@ struct OptPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { std::string opt_clean_args; - std::string opt_const_args; + std::string opt_expr_args; std::string opt_reduce_args; + std::string opt_merge_args; bool fast_mode = false; - log_header("Executing OPT pass (performing simple optimizations).\n"); + log_header(design, "Executing OPT pass (performing simple optimizations).\n"); log_push(); size_t argidx; @@ -80,29 +81,37 @@ struct OptPass : public Pass { continue; } if (args[argidx] == "-mux_undef") { - opt_const_args += " -mux_undef"; + opt_expr_args += " -mux_undef"; continue; } if (args[argidx] == "-mux_bool") { - opt_const_args += " -mux_bool"; + opt_expr_args += " -mux_bool"; continue; } if (args[argidx] == "-undriven") { - opt_const_args += " -undriven"; + opt_expr_args += " -undriven"; + continue; + } + if (args[argidx] == "-clkinv") { + opt_expr_args += " -clkinv"; continue; } if (args[argidx] == "-fine") { - opt_const_args += " -fine"; + opt_expr_args += " -fine"; opt_reduce_args += " -fine"; continue; } if (args[argidx] == "-full") { - opt_const_args += " -full"; + opt_expr_args += " -full"; opt_reduce_args += " -full"; continue; } if (args[argidx] == "-keepdc") { - opt_const_args += " -keepdc"; + opt_expr_args += " -keepdc"; + continue; + } + if (args[argidx] == "-share_all") { + opt_merge_args += " -share_all"; continue; } if (args[argidx] == "-fast") { @@ -116,38 +125,42 @@ struct OptPass : public Pass { if (fast_mode) { while (1) { - Pass::call(design, "opt_const" + opt_const_args); - Pass::call(design, "opt_share"); + Pass::call(design, "opt_expr" + opt_expr_args); + Pass::call(design, "opt_merge" + opt_merge_args); design->scratchpad_unset("opt.did_something"); Pass::call(design, "opt_rmdff"); if (design->scratchpad_get_bool("opt.did_something") == false) break; Pass::call(design, "opt_clean" + opt_clean_args); - log_header("Rerunning OPT passes. (Removed registers in this run.)\n"); + log_header(design, "Rerunning OPT passes. (Removed registers in this run.)\n"); } Pass::call(design, "opt_clean" + opt_clean_args); } else { - Pass::call(design, "opt_const" + opt_const_args); - Pass::call(design, "opt_share -nomux"); + Pass::call(design, "opt_expr" + opt_expr_args); + Pass::call(design, "opt_merge -nomux" + opt_merge_args); while (1) { design->scratchpad_unset("opt.did_something"); Pass::call(design, "opt_muxtree"); Pass::call(design, "opt_reduce" + opt_reduce_args); - Pass::call(design, "opt_share"); + Pass::call(design, "opt_merge" + opt_merge_args); Pass::call(design, "opt_rmdff"); Pass::call(design, "opt_clean" + opt_clean_args); - Pass::call(design, "opt_const" + opt_const_args); + Pass::call(design, "opt_expr" + opt_expr_args); if (design->scratchpad_get_bool("opt.did_something") == false) break; - log_header("Rerunning OPT passes. (Maybe there is more to do..)\n"); + log_header(design, "Rerunning OPT passes. (Maybe there is more to do..)\n"); } } - log_header(fast_mode ? "Finished fast OPT passes.\n" : "Finished OPT passes. (There is nothing left to do.)\n"); + design->optimize(); + design->sort(); + design->check(); + + log_header(design, fast_mode ? "Finished fast OPT passes.\n" : "Finished OPT passes. (There is nothing left to do.)\n"); log_pop(); } } OptPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 6a7e6051d..6600ffa25 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -2,11 +2,11 @@ * 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 @@ -30,7 +30,55 @@ PRIVATE_NAMESPACE_BEGIN using RTLIL::id2cstr; -CellTypes ct, ct_reg, ct_all; +struct keep_cache_t +{ + Design *design; + dict<Module*, bool> cache; + + void reset(Design *design = nullptr) + { + this->design = design; + cache.clear(); + } + + bool query(Module *module) + { + log_assert(design != nullptr); + + if (module == nullptr) + return false; + + if (cache.count(module)) + return cache.at(module); + + cache[module] = true; + if (!module->get_bool_attribute("\\keep")) { + bool found_keep = false; + for (auto cell : module->cells()) + if (query(cell)) found_keep = true; + cache[module] = found_keep; + } + + return cache[module]; + } + + bool query(Cell *cell) + { + if (cell->type.in("$memwr", "$meminit", "$assert", "$assume")) + return true; + + if (cell->has_keep_attr()) + return true; + + if (cell->module && cell->module->design) + return query(cell->module->design->module(cell->type)); + + return false; + } +}; + +keep_cache_t keep_cache; +CellTypes ct_reg, ct_all; int count_rm_cells, count_rm_wires; void rmunused_module_cells(Module *module, bool verbose) @@ -42,12 +90,12 @@ void rmunused_module_cells(Module *module, bool verbose) for (auto &it : module->cells_) { Cell *cell = it.second; for (auto &it2 : cell->connections()) { - if (!ct.cell_input(cell->type, it2.first)) + if (!ct_all.cell_known(cell->type) || ct_all.cell_output(cell->type, it2.first)) for (auto bit : sigmap(it2.second)) if (bit.wire != nullptr) wire2driver[bit].insert(cell); } - if (cell->type == "$memwr" || cell->type == "$assert" || cell->has_keep_attr()) + if (keep_cache.query(cell)) queue.insert(cell); else unused.insert(cell); @@ -67,7 +115,7 @@ void rmunused_module_cells(Module *module, bool verbose) pool<SigBit> bits; for (auto cell : queue) for (auto &it : cell->connections()) - if (!ct.cell_output(cell->type, it.first)) + if (!ct_all.cell_known(cell->type) || ct_all.cell_input(cell->type, it.first)) for (auto bit : sigmap(it.second)) bits.insert(bit); @@ -108,6 +156,9 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPoo if (w1->port_input != w2->port_input) return w2->port_input; + if ((w1->port_input && w1->port_output) != (w2->port_input && w2->port_output)) + return !(w2->port_input && w2->port_output); + if (w1->name[0] == '\\' && w2->name[0] == '\\') { if (regs.check_any(s1) != regs.check_any(s2)) return regs.check_any(s2); @@ -129,7 +180,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) @@ -159,7 +210,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos for (auto &it2 : cell->connections()) connected_signals.add(it2.second); } - + SigMap assign_map(module); pool<RTLIL::SigSpec> direct_sigs; pool<RTLIL::Wire*> direct_wires; @@ -193,7 +244,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos for (auto &it2 : cell->connections_) { assign_map.apply(it2.second); used_signals.add(it2.second); - if (!ct.cell_output(cell->type, it2.first)) + if (!ct_all.cell_output(cell->type, it2.first)) used_signals_nodrivers.add(it2.second); } } @@ -216,7 +267,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos std::vector<RTLIL::Wire*> maybe_del_wires; for (auto wire : module->wires()) { - if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep")) { + if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep") || wire->attributes.count("\\init")) { RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1; assign_map.apply(s2); if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep")) { @@ -296,8 +347,14 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose) module->connect(y, a); delcells.push_back(cell); } - for (auto cell : delcells) + for (auto cell : delcells) { + if (verbose) + log(" removing buffer cell `%s': %s = %s\n", cell->name.c_str(), + log_signal(cell->getPort("\\Y")), log_signal(cell->getPort("\\A"))); module->remove(cell); + } + if (!delcells.empty()) + module->design->scratchpad_set_bool("opt.did_something", true); rmunused_module_cells(module, verbose); rmunused_module_signals(module, purge_mode, verbose); @@ -326,7 +383,7 @@ struct OptCleanPass : public Pass { { bool purge_mode = false; - log_header("Executing OPT_CLEAN pass (remove unused cells and wires).\n"); + log_header(design, "Executing OPT_CLEAN pass (remove unused cells and wires).\n"); log_push(); size_t argidx; @@ -339,26 +396,30 @@ struct OptCleanPass : public Pass { } extra_args(args, argidx, design); - ct.setup_internals(); - ct.setup_internals_mem(); - ct.setup_stdcells(); - ct.setup_stdcells_mem(); + keep_cache.reset(design); ct_reg.setup_internals_mem(); ct_reg.setup_stdcells_mem(); + ct_all.setup(design); + for (auto module : design->selected_whole_modules_warn()) { if (module->has_processes_warn()) continue; rmunused_module(module, purge_mode, true); } - ct.clear(); + design->optimize(); + design->sort(); + design->check(); + + keep_cache.reset(); ct_reg.clear(); + ct_all.clear(); log_pop(); } } OptCleanPass; - + struct CleanPass : public Pass { CleanPass() : Pass("clean", "remove unused cells and wires") { } virtual void help() @@ -391,10 +452,7 @@ struct CleanPass : public Pass { if (argidx < args.size()) extra_args(args, argidx, design); - ct.setup_internals(); - ct.setup_internals_mem(); - ct.setup_stdcells(); - ct.setup_stdcells_mem(); + keep_cache.reset(design); ct_reg.setup_internals_mem(); ct_reg.setup_stdcells_mem(); @@ -404,13 +462,10 @@ struct CleanPass : public Pass { count_rm_cells = 0; count_rm_wires = 0; - for (auto mod : design->selected_whole_modules()) { - if (mod->has_processes()) + for (auto module : design->selected_whole_modules()) { + if (module->has_processes()) continue; - do { - design->scratchpad_unset("opt.did_something"); - rmunused_module(mod, purge_mode, false); - } while (design->scratchpad_get_bool("opt.did_something")); + rmunused_module(module, purge_mode, false); } if (count_rm_cells > 0 || count_rm_wires > 0) @@ -420,10 +475,10 @@ struct CleanPass : public Pass { design->sort(); design->check(); - ct.clear(); + keep_cache.reset(); ct_reg.clear(); ct_all.clear(); } } CleanPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_const.cc b/passes/opt/opt_expr.cc index 1758a34fa..b62eae285 100644 --- a/passes/opt/opt_const.cc +++ b/passes/opt/opt_expr.cc @@ -2,11 +2,11 @@ * 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 @@ -179,14 +179,87 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ log("\n"); } - cover_list("opt.opt_const.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str()); + cover_list("opt.opt_expr.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str()); module->remove(cell); did_something = true; return true; } -void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc) +void handle_polarity_inv(Cell *cell, IdString port, IdString param, const SigMap &assign_map, const dict<RTLIL::SigSpec, RTLIL::SigSpec> &invert_map) +{ + SigSpec sig = assign_map(cell->getPort(port)); + if (invert_map.count(sig)) { + log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n", + log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module), + log_signal(sig), log_signal(invert_map.at(sig))); + cell->setPort(port, (invert_map.at(sig))); + cell->setParam(param, !cell->getParam(param).as_bool()); + } +} + +void handle_clkpol_celltype_swap(Cell *cell, string type1, string type2, IdString port, const SigMap &assign_map, const dict<RTLIL::SigSpec, RTLIL::SigSpec> &invert_map) +{ + log_assert(GetSize(type1) == GetSize(type2)); + string cell_type = cell->type.str(); + + if (GetSize(type1) != GetSize(cell_type)) + return; + + for (int i = 0; i < GetSize(type1); i++) { + log_assert((type1[i] == '?') == (type2[i] == '?')); + if (type1[i] == '?') { + if (cell_type[i] != '0' && cell_type[i] != '1' && cell_type[i] != 'N' && cell_type[i] != 'P') + return; + type1[i] = cell_type[i]; + type2[i] = cell_type[i]; + } + } + + if (cell->type.in(type1, type2)) { + SigSpec sig = assign_map(cell->getPort(port)); + if (invert_map.count(sig)) { + log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n", + log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module), + log_signal(sig), log_signal(invert_map.at(sig))); + cell->setPort(port, (invert_map.at(sig))); + cell->type = cell->type == type1 ? type2 : type1; + } + } +} + +bool is_one_or_minus_one(const Const &value, bool is_signed, bool &is_negative) +{ + bool all_bits_one = true; + bool last_bit_one = true; + + if (GetSize(value.bits) < 1) + return false; + + if (GetSize(value.bits) == 1) { + if (value.bits[0] != State::S1) + return false; + if (is_signed) + is_negative = true; + return true; + } + + for (int i = 0; i < GetSize(value.bits); i++) { + if (value.bits[i] != State::S1) + all_bits_one = false; + if (value.bits[i] != (i ? State::S0 : State::S1)) + last_bit_one = false; + } + + if (all_bits_one && is_signed) { + is_negative = true; + return true; + } + + return last_bit_one; +} + +void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool clkinv) { if (!design->selected(module)) return; @@ -207,6 +280,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") && cell->getPort("\\A").size() == 1 && cell->getPort("\\Y").size() == 1) invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\A")); + if ((cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == SigSpec(State::S1) && cell->getPort("\\B") == SigSpec(State::S0)) + invert_map[assign_map(cell->getPort("\\Y"))] = assign_map(cell->getPort("\\S")); if (ct_combinational.cell_known(cell->type)) for (auto &conn : cell->connections()) { RTLIL::SigSpec sig = assign_map(conn.second); @@ -229,9 +304,106 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons for (auto cell : cells.sorted) { -#define ACTION_DO(_p_, _s_) do { cover("opt.opt_const.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) +#define ACTION_DO(_p_, _s_) do { cover("opt.opt_expr.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) #define ACTION_DO_Y(_v_) ACTION_DO("\\Y", RTLIL::SigSpec(RTLIL::State::S ## _v_)) + if (clkinv) + { + if (cell->type.in("$dff", "$dffe", "$dffsr", "$adff", "$fsm", "$memrd", "$memwr")) + handle_polarity_inv(cell, "\\CLK", "\\CLK_POLARITY", assign_map, invert_map); + + if (cell->type.in("$sr", "$dffsr", "$dlatchsr")) { + handle_polarity_inv(cell, "\\SET", "\\SET_POLARITY", assign_map, invert_map); + handle_polarity_inv(cell, "\\CLR", "\\CLR_POLARITY", assign_map, invert_map); + } + + if (cell->type.in("$dffe", "$dlatch", "$dlatchsr")) + handle_polarity_inv(cell, "\\EN", "\\EN_POLARITY", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_SR_N?_", "$_SR_P?_", "\\S", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_SR_?N_", "$_SR_?P_", "\\R", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DFF_N_", "$_DFF_P_", "\\C", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DFFE_N?_", "$_DFFE_P?_", "\\C", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFE_?N_", "$_DFFE_?P_", "\\E", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DFF_N??_", "$_DFF_P??_", "\\C", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFF_?N?_", "$_DFF_?P?_", "\\R", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DFFSR_N??_", "$_DFFSR_P??_", "\\C", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFSR_?N?_", "$_DFFSR_?P?_", "\\S", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DFFSR_??N_", "$_DFFSR_??P_", "\\R", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DLATCH_N_", "$_DLATCH_P_", "\\E", assign_map, invert_map); + + handle_clkpol_celltype_swap(cell, "$_DLATCHSR_N??_", "$_DLATCHSR_P??_", "\\E", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DLATCHSR_?N?_", "$_DLATCHSR_?P?_", "\\S", assign_map, invert_map); + handle_clkpol_celltype_swap(cell, "$_DLATCHSR_??N_", "$_DLATCHSR_??P_", "\\R", assign_map, invert_map); + } + + bool detect_const_and = false; + bool detect_const_or = false; + + if (cell->type.in("$reduce_and", "$_AND_")) + detect_const_and = true; + + if (cell->type.in("$and", "$logic_and") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1) + detect_const_and = true; + + if (cell->type.in("$reduce_or", "$reduce_bool", "$_OR_")) + detect_const_or = true; + + if (cell->type.in("$or", "$logic_or") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1) + detect_const_or = true; + + if (detect_const_and || detect_const_or) + { + pool<SigBit> input_bits = assign_map(cell->getPort("\\A")).to_sigbit_pool(); + bool found_zero = false, found_one = false, found_inv = false; + + if (cell->hasPort("\\B")) { + vector<SigBit> more_bits = assign_map(cell->getPort("\\B")).to_sigbit_vector(); + input_bits.insert(more_bits.begin(), more_bits.end()); + } + + for (auto bit : input_bits) { + if (bit == State::S0) + found_zero = true; + if (bit == State::S1) + found_one = true; + if (invert_map.count(bit) && input_bits.count(invert_map.at(bit))) + found_inv = true; + } + + if (detect_const_and && (found_zero || found_inv)) { + cover("opt.opt_expr.const_and"); + replace_cell(assign_map, module, cell, "const_and", "\\Y", RTLIL::State::S0); + goto next_cell; + } + + if (detect_const_or && (found_one || found_inv)) { + cover("opt.opt_expr.const_or"); + replace_cell(assign_map, module, cell, "const_or", "\\Y", RTLIL::State::S1); + goto next_cell; + } + } + + if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool", "$reduce_xor", "$reduce_xnor", "$neg") && + GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\Y")) == 1) + { + if (cell->type == "$reduce_xnor") { + cover("opt.opt_expr.reduce_xnor_not"); + log("Replacing %s cell `%s' in module `%s' with $not cell.\n", + log_id(cell->type), log_id(cell->name), log_id(module)); + cell->type = "$not"; + } else { + cover("opt.opt_expr.unary_buffer"); + replace_cell(assign_map, module, cell, "unary_buffer", "\\Y", cell->getPort("\\A")); + } + goto next_cell; + } + if (do_fine) { if (cell->type == "$not" || cell->type == "$pos" || @@ -256,7 +428,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) { - cover("opt.opt_const.fine.$reduce_and"); + cover("opt.opt_expr.fine.$reduce_and"); log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a)); cell->setPort("\\A", sig_a = new_a); @@ -282,7 +454,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) { - cover_list("opt.opt_const.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str()); + cover_list("opt.opt_expr.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str()); log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a)); cell->setPort("\\A", sig_a = new_a); @@ -308,7 +480,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_b != RTLIL::State::Sm && RTLIL::SigSpec(new_b) != sig_b) { - cover_list("opt.opt_const.fine.B", "$logic_and", "$logic_or", cell->type.str()); + cover_list("opt.opt_expr.fine.B", "$logic_and", "$logic_or", cell->type.str()); log("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_b)); cell->setPort("\\B", sig_b = new_b); @@ -318,18 +490,6 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } - if (cell->type == "$logic_or" && (assign_map(cell->getPort("\\A")) == RTLIL::State::S1 || assign_map(cell->getPort("\\B")) == RTLIL::State::S1)) { - cover("opt.opt_const.one_high"); - replace_cell(assign_map, module, cell, "one high", "\\Y", RTLIL::State::S1); - goto next_cell; - } - - if (cell->type == "$logic_and" && (assign_map(cell->getPort("\\A")) == RTLIL::State::S0 || assign_map(cell->getPort("\\B")) == RTLIL::State::S0)) { - cover("opt.opt_const.one_low"); - replace_cell(assign_map, module, cell, "one low", "\\Y", RTLIL::State::S0); - goto next_cell; - } - if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" || cell->type == "$shift" || cell->type == "$shiftx" || cell->type == "$shl" || cell->type == "$shr" || cell->type == "$sshl" || cell->type == "$sshr" || cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt" || @@ -352,7 +512,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (0) { found_the_x_bit: - cover_list("opt.opt_const.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", + cover_list("opt.opt_expr.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$lt", "$le", "$ge", "$gt", "$neg", "$add", "$sub", "$mul", "$div", "$mod", "$pow", cell->type.str()); if (cell->type == "$reduce_xor" || cell->type == "$reduce_xnor" || cell->type == "$lt" || cell->type == "$le" || cell->type == "$ge" || cell->type == "$gt") @@ -365,13 +525,13 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if ((cell->type == "$_NOT_" || cell->type == "$not" || cell->type == "$logic_not") && cell->getPort("\\Y").size() == 1 && invert_map.count(assign_map(cell->getPort("\\A"))) != 0) { - cover_list("opt.opt_const.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str()); + cover_list("opt.opt_expr.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str()); replace_cell(assign_map, module, cell, "double_invert", "\\Y", invert_map.at(assign_map(cell->getPort("\\A")))); goto next_cell; } if ((cell->type == "$_MUX_" || cell->type == "$mux") && invert_map.count(assign_map(cell->getPort("\\S"))) != 0) { - cover_list("opt.opt_const.invert.muxsel", "$_MUX_", "$mux", cell->type.str()); + cover_list("opt.opt_expr.invert.muxsel", "$_MUX_", "$mux", cell->type.str()); log("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module)); RTLIL::SigSpec tmp = cell->getPort("\\A"); cell->setPort("\\A", cell->getPort("\\B")); @@ -454,7 +614,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (input.match(" 1")) ACTION_DO("\\Y", input.extract(1, 1)); if (input.match("01 ")) ACTION_DO("\\Y", input.extract(0, 1)); if (input.match("10 ")) { - cover("opt.opt_const.mux_to_inv"); + cover("opt.opt_expr.mux_to_inv"); cell->type = "$_NOT_"; cell->setPort("\\A", input.extract(0, 1)); cell->unsetPort("\\B"); @@ -479,7 +639,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()); } @@ -489,7 +649,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons log_assert(GetSize(a) == GetSize(b)); for (int i = 0; i < GetSize(a); i++) { if (a[i].wire == NULL && b[i].wire == NULL && a[i] != b[i] && a[i].data <= RTLIL::State::S1 && b[i].data <= RTLIL::State::S1) { - cover_list("opt.opt_const.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); + cover_list("opt.opt_expr.eqneq.isneq", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S0 : RTLIL::State::S1); new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false); replace_cell(assign_map, module, cell, "isneq", "\\Y", new_y); @@ -502,7 +662,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_a.size() == 0) { - cover_list("opt.opt_const.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); + cover_list("opt.opt_expr.eqneq.empty", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); RTLIL::SigSpec new_y = RTLIL::SigSpec((cell->type == "$eq" || cell->type == "$eqx") ? RTLIL::State::S1 : RTLIL::State::S0); new_y.extend_u0(cell->parameters["\\Y_WIDTH"].as_int(), false); replace_cell(assign_map, module, cell, "empty", "\\Y", new_y); @@ -510,7 +670,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (new_a.size() < a.size() || new_b.size() < b.size()) { - cover_list("opt.opt_const.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); + cover_list("opt.opt_expr.eqneq.resize", "$eq", "$ne", "$eqx", "$nex", cell->type.str()); cell->setPort("\\A", new_a); cell->setPort("\\B", new_b); cell->parameters["\\A_WIDTH"] = new_a.size(); @@ -525,7 +685,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); if (a.is_fully_const() && !b.is_fully_const()) { - cover_list("opt.opt_const.eqneq.swapconst", "$eq", "$ne", cell->type.str()); + cover_list("opt.opt_expr.eqneq.swapconst", "$eq", "$ne", cell->type.str()); cell->setPort("\\A", b); cell->setPort("\\B", a); std::swap(a, b); @@ -536,7 +696,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec input = b; ACTION_DO("\\Y", cell->getPort("\\A")); } else { - cover_list("opt.opt_const.eqneq.isnot", "$eq", "$ne", cell->type.str()); + cover_list("opt.opt_expr.eqneq.isnot", "$eq", "$ne", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->type = "$not"; cell->parameters.erase("\\B_WIDTH"); @@ -548,6 +708,25 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } + if ((cell->type == "$eq" || cell->type == "$ne") && + (assign_map(cell->getPort("\\A")).is_fully_zero() || assign_map(cell->getPort("\\B")).is_fully_zero())) + { + cover_list("opt.opt_expr.eqneq.cmpzero", "$eq", "$ne", cell->type.str()); + log("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell), + log_id(module), "$eq" ? "$logic_not" : "$reduce_bool"); + cell->type = cell->type == "$eq" ? "$logic_not" : "$reduce_bool"; + if (assign_map(cell->getPort("\\A")).is_fully_zero()) { + cell->setPort("\\A", cell->getPort("\\B")); + cell->setParam("\\A_SIGNED", cell->getParam("\\B_SIGNED")); + cell->setParam("\\A_WIDTH", cell->getParam("\\B_WIDTH")); + } + cell->unsetPort("\\B"); + cell->unsetParam("\\B_SIGNED"); + cell->unsetParam("\\B_WIDTH"); + did_something = true; + goto next_cell; + } + if (cell->type.in("$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx") && assign_map(cell->getPort("\\B")).is_fully_const()) { bool sign_ext = cell->type == "$sshr" && cell->getParam("\\A_SIGNED").as_bool(); @@ -570,7 +749,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons sig_y[i] = sig_a[GetSize(sig_a)-1]; } - cover_list("opt.opt_const.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str()); + cover_list("opt.opt_expr.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str()); log("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n", log_id(cell->type), log_id(cell), log_signal(assign_map(cell->getPort("\\B"))), shift_bits, log_id(module), log_signal(sig_y)); @@ -586,6 +765,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons { bool identity_wrt_a = false; bool identity_wrt_b = false; + bool arith_inverse = false; if (cell->type == "$add" || cell->type == "$sub" || cell->type == "$or" || cell->type == "$xor") { @@ -612,10 +792,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec a = assign_map(cell->getPort("\\A")); RTLIL::SigSpec b = assign_map(cell->getPort("\\B")); - if (a.is_fully_const() && a.size() <= 32 && a.as_int() == 1) + if (a.is_fully_const() && is_one_or_minus_one(a.as_const(), cell->getParam("\\A_SIGNED").as_bool(), arith_inverse)) identity_wrt_b = true; - - if (b.is_fully_const() && b.size() <= 32 && b.as_int() == 1) + else + if (b.is_fully_const() && is_one_or_minus_one(b.as_const(), cell->getParam("\\B_SIGNED").as_bool(), arith_inverse)) identity_wrt_a = true; } @@ -630,9 +810,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (identity_wrt_a || identity_wrt_b) { if (identity_wrt_a) - cover_list("opt.opt_const.identwrt.a", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); + cover_list("opt.opt_expr.identwrt.a", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); if (identity_wrt_b) - cover_list("opt.opt_const.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); + cover_list("opt.opt_expr.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with identity for port %c.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str(), identity_wrt_a ? 'A' : 'B'); @@ -643,7 +823,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons cell->parameters.at("\\A_SIGNED") = cell->parameters.at("\\B_SIGNED"); } - cell->type = "$pos"; + cell->type = arith_inverse ? "$neg" : "$pos"; cell->unsetPort("\\B"); cell->parameters.erase("\\B_WIDTH"); cell->parameters.erase("\\B_SIGNED"); @@ -656,14 +836,14 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1) && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) { - cover_list("opt.opt_const.mux_bool", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_bool", "$mux", "$_MUX_", cell->type.str()); replace_cell(assign_map, module, cell, "mux_bool", "\\Y", cell->getPort("\\S")); goto next_cell; } if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(1, 1) && cell->getPort("\\B") == RTLIL::SigSpec(0, 1)) { - cover_list("opt.opt_const.mux_invert", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_invert", "$mux", "$_MUX_", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\A", cell->getPort("\\S")); cell->unsetPort("\\B"); @@ -682,7 +862,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1)) { - cover_list("opt.opt_const.mux_and", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_and", "$mux", "$_MUX_", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\A", cell->getPort("\\S")); cell->unsetPort("\\S"); @@ -702,7 +882,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) { - cover_list("opt.opt_const.mux_or", "$mux", "$_MUX_", cell->type.str()); + cover_list("opt.opt_expr.mux_or", "$mux", "$_MUX_", cell->type.str()); log("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\B", cell->getPort("\\S")); cell->unsetPort("\\S"); @@ -726,7 +906,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons int width = cell->getPort("\\A").size(); if ((cell->getPort("\\A").is_fully_undef() && cell->getPort("\\B").is_fully_undef()) || cell->getPort("\\S").is_fully_undef()) { - cover_list("opt.opt_const.mux_undef", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_undef", "$mux", "$pmux", cell->type.str()); replace_cell(assign_map, module, cell, "mux_undef", "\\Y", cell->getPort("\\A")); goto next_cell; } @@ -745,17 +925,17 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons new_s = new_s.extract(0, new_s.size()-1); } if (new_s.size() == 0) { - cover_list("opt.opt_const.mux_empty", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_empty", "$mux", "$pmux", cell->type.str()); replace_cell(assign_map, module, cell, "mux_empty", "\\Y", new_a); goto next_cell; } if (new_a == RTLIL::SigSpec(RTLIL::State::S0) && new_b == RTLIL::SigSpec(RTLIL::State::S1)) { - cover_list("opt.opt_const.mux_sel01", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_sel01", "$mux", "$pmux", cell->type.str()); replace_cell(assign_map, module, cell, "mux_sel01", "\\Y", new_s); goto next_cell; } if (cell->getPort("\\S").size() != new_s.size()) { - cover_list("opt.opt_const.mux_reduce", "$mux", "$pmux", cell->type.str()); + cover_list("opt.opt_expr.mux_reduce", "$mux", "$pmux", cell->type.str()); log("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n", GetSize(cell->getPort("\\S")) - GetSize(new_s), log_id(cell->type), log_id(cell), log_id(module)); cell->setPort("\\A", new_a); @@ -781,7 +961,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), dummy_arg, \ cell->parameters["\\A_SIGNED"].as_bool(), false, \ cell->parameters["\\Y_WIDTH"].as_int())); \ - cover("opt.opt_const.const.$" #_t); \ + cover("opt.opt_expr.const.$" #_t); \ replace_cell(assign_map, module, cell, stringf("%s", log_signal(a)), "\\Y", y); \ goto next_cell; \ } \ @@ -796,7 +976,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons cell->parameters["\\A_SIGNED"].as_bool(), \ cell->parameters["\\B_SIGNED"].as_bool(), \ cell->parameters["\\Y_WIDTH"].as_int())); \ - cover("opt.opt_const.const.$" #_t); \ + cover("opt.opt_expr.const.$" #_t); \ replace_cell(assign_map, module, cell, stringf("%s, %s", log_signal(a), log_signal(b)), "\\Y", y); \ goto next_cell; \ } \ @@ -872,7 +1052,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (a_val == 0) { - cover("opt.opt_const.mul_shift.zero"); + cover("opt.opt_expr.mul_shift.zero"); log("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n", cell->name.c_str(), module->name.c_str()); @@ -888,9 +1068,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (a_val == (1 << i)) { if (swapped_ab) - cover("opt.opt_const.mul_shift.swapped"); + cover("opt.opt_expr.mul_shift.swapped"); else - cover("opt.opt_const.mul_shift.unswapped"); + cover("opt.opt_expr.mul_shift.unswapped"); log("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n", a_val, cell->name.c_str(), module->name.c_str(), i); @@ -918,6 +1098,75 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } + if (!keepdc && cell->type.in("$div", "$mod")) + { + bool b_signed = cell->parameters["\\B_SIGNED"].as_bool(); + SigSpec sig_b = assign_map(cell->getPort("\\B")); + SigSpec sig_y = assign_map(cell->getPort("\\Y")); + + if (sig_b.is_fully_def() && sig_b.size() <= 32) + { + int b_val = sig_b.as_int(); + + if (b_val == 0) + { + cover("opt.opt_expr.divmod_zero"); + + log("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n", + cell->name.c_str(), module->name.c_str()); + + module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(State::Sx, sig_y.size()))); + module->remove(cell); + + did_something = true; + goto next_cell; + } + + for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++) + if (b_val == (1 << i)) + { + if (cell->type == "$div") + { + cover("opt.opt_expr.div_shift"); + + log("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n", + b_val, cell->name.c_str(), module->name.c_str(), i); + + std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6); + + while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0) + new_b.pop_back(); + + cell->type = "$shr"; + cell->parameters["\\B_WIDTH"] = GetSize(new_b); + cell->parameters["\\B_SIGNED"] = false; + cell->setPort("\\B", new_b); + cell->check(); + } + else + { + cover("opt.opt_expr.mod_mask"); + + log("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n", + b_val, cell->name.c_str(), module->name.c_str()); + + std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i); + + if (b_signed) + new_b.push_back(State::S0); + + cell->type = "$and"; + cell->parameters["\\B_WIDTH"] = GetSize(new_b); + cell->setPort("\\B", new_b); + cell->check(); + } + + did_something = true; + goto next_cell; + } + } + } + next_cell:; #undef ACTION_DO #undef ACTION_DO_Y @@ -926,15 +1175,16 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } -struct OptConstPass : public Pass { - OptConstPass() : Pass("opt_const", "perform const folding") { } +struct OptExprPass : public Pass { + OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" opt_const [options] [selection]\n"); + log(" opt_expr [options] [selection]\n"); log("\n"); log("This pass performs const folding on internal cell types with constant inputs.\n"); + log("It also performs some simple expression rewritring.\n"); log("\n"); log(" -mux_undef\n"); log(" remove 'undef' inputs from $mux, $pmux and $_MUX_ cells\n"); @@ -945,6 +1195,9 @@ struct OptConstPass : public Pass { log(" -undriven\n"); log(" replace undriven nets with undef (x) constants\n"); log("\n"); + log(" -clkinv\n"); + log(" optimize clock inverters by changing FF types\n"); + log("\n"); log(" -fine\n"); log(" perform fine-grain optimizations\n"); log("\n"); @@ -963,10 +1216,11 @@ struct OptConstPass : public Pass { bool mux_undef = false; bool mux_bool = false; bool undriven = false; + bool clkinv = false; bool do_fine = false; bool keepdc = false; - log_header("Executing OPT_CONST pass (perform const folding).\n"); + log_header(design, "Executing OPT_EXPR pass (perform const folding).\n"); log_push(); size_t argidx; @@ -983,6 +1237,10 @@ struct OptConstPass : public Pass { undriven = true; continue; } + if (args[argidx] == "-clkinv") { + clkinv = true; + continue; + } if (args[argidx] == "-fine") { do_fine = true; continue; @@ -1010,16 +1268,16 @@ struct OptConstPass : public Pass { do { do { did_something = false; - replace_const_cells(design, module, false, mux_undef, mux_bool, do_fine, keepdc); + replace_const_cells(design, module, false, mux_undef, mux_bool, do_fine, keepdc, clkinv); if (did_something) design->scratchpad_set_bool("opt.did_something", true); } while (did_something); - replace_const_cells(design, module, true, mux_undef, mux_bool, do_fine, keepdc); + replace_const_cells(design, module, true, mux_undef, mux_bool, do_fine, keepdc, clkinv); } while (did_something); } log_pop(); } -} OptConstPass; +} OptExprPass; PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_merge.cc index cce97d65b..97989d271 100644 --- a/passes/opt/opt_share.cc +++ b/passes/opt/opt_merge.cc @@ -2,11 +2,11 @@ * 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 @@ -31,12 +31,13 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -struct OptShareWorker +struct OptMergeWorker { RTLIL::Design *design; RTLIL::Module *module; SigMap assign_map; SigMap dff_init_map; + bool mode_share_all; CellTypes ct; int total_count; @@ -44,6 +45,29 @@ struct OptShareWorker dict<const RTLIL::Cell*, std::string> cell_hash_cache; #endif + static void sort_pmux_conn(dict<RTLIL::IdString, RTLIL::SigSpec> &conn) + { + SigSpec sig_s = conn.at("\\S"); + SigSpec sig_b = conn.at("\\B"); + + int s_width = GetSize(sig_s); + int width = GetSize(sig_b) / s_width; + + vector<pair<SigBit, SigSpec>> sb_pairs; + for (int i = 0; i < s_width; i++) + sb_pairs.push_back(pair<SigBit, SigSpec>(sig_s[i], sig_b.extract(i*width, width))); + + std::sort(sb_pairs.begin(), sb_pairs.end()); + + conn["\\S"] = SigSpec(); + conn["\\B"] = SigSpec(); + + for (auto &it : sb_pairs) { + conn["\\S"].append(it.first); + conn["\\B"].append(it.second); + } + } + #ifdef USE_CELL_HASH_CACHE std::string int_to_hash_string(unsigned int v) { @@ -90,25 +114,40 @@ struct OptShareWorker assign_map.apply(alt_conn.at("\\A")); alt_conn.at("\\A").sort_and_unify(); conn = &alt_conn; + } else + if (cell->type == "$pmux") { + alt_conn = *conn; + assign_map.apply(alt_conn.at("\\A")); + assign_map.apply(alt_conn.at("\\B")); + assign_map.apply(alt_conn.at("\\S")); + sort_pmux_conn(alt_conn); + conn = &alt_conn; } + vector<string> hash_conn_strings; + for (auto &it : *conn) { - if (ct.cell_output(cell->type, it.first)) + if (cell->output(it.first)) continue; RTLIL::SigSpec sig = it.second; assign_map.apply(sig); - hash_string += "C " + it.first.str() + "="; + string s = "C " + it.first.str() + "="; for (auto &chunk : sig.chunks()) { if (chunk.wire) - hash_string += "{" + chunk.wire->name.str() + " " + + s += "{" + chunk.wire->name.str() + " " + int_to_hash_string(chunk.offset) + " " + int_to_hash_string(chunk.width) + "}"; else - hash_string += RTLIL::Const(chunk.data).as_string(); + s += RTLIL::Const(chunk.data).as_string(); } - hash_string += "\n"; + hash_conn_strings.push_back(s + "\n"); } + std::sort(hash_conn_strings.begin(), hash_conn_strings.end()); + + for (auto it : hash_conn_strings) + hash_string += it; + cell_hash_cache[cell] = sha1(hash_string); return cell_hash_cache[cell]; } @@ -137,14 +176,14 @@ struct OptShareWorker dict<RTLIL::IdString, RTLIL::SigSpec> conn2 = cell2->connections(); for (auto &it : conn1) { - if (ct.cell_output(cell1->type, it.first)) + if (cell1->output(it.first)) it.second = RTLIL::SigSpec(); else assign_map.apply(it.second); } for (auto &it : conn2) { - if (ct.cell_output(cell2->type, it.first)) + if (cell2->output(it.first)) it.second = RTLIL::SigSpec(); else assign_map.apply(it.second); @@ -170,6 +209,10 @@ struct OptShareWorker if (cell1->type == "$reduce_and" || cell1->type == "$reduce_or" || cell1->type == "$reduce_bool") { conn1["\\A"].sort_and_unify(); conn2["\\A"].sort_and_unify(); + } else + if (cell1->type == "$pmux") { + sort_pmux_conn(conn1); + sort_pmux_conn(conn2); } if (conn1 != conn2) { @@ -197,7 +240,7 @@ struct OptShareWorker if (cell1->type != cell2->type) return cell1->type < cell2->type; - if (!ct.cell_known(cell1->type)) + if ((!mode_share_all && !ct.cell_known(cell1->type)) || !cell1->known()) return cell1 < cell2; if (cell1->has_keep_attr() || cell2->has_keep_attr()) @@ -211,15 +254,15 @@ struct OptShareWorker } struct CompareCells { - OptShareWorker *that; - CompareCells(OptShareWorker *that) : that(that) {} + OptMergeWorker *that; + CompareCells(OptMergeWorker *that) : that(that) {} bool operator()(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) const { return that->compare_cells(cell1, cell2); } }; - OptShareWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux) : - design(design), module(module), assign_map(module) + OptMergeWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all) : + design(design), module(module), assign_map(module), mode_share_all(mode_share_all) { total_count = 0; ct.setup_internals(); @@ -249,7 +292,9 @@ struct OptShareWorker std::vector<RTLIL::Cell*> cells; cells.reserve(module->cells_.size()); for (auto &it : module->cells_) { - if (ct.cell_known(it.second->type) && design->selected(module, it.second)) + if (!design->selected(module, it.second)) + continue; + if (ct.cell_known(it.second->type) || (mode_share_all && it.second->known())) cells.push_back(it.second); } @@ -261,7 +306,7 @@ struct OptShareWorker did_something = true; log(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str()); for (auto &it : cell->connections()) { - if (ct.cell_output(cell->type, it.first)) { + if (cell->output(it.first)) { RTLIL::SigSpec other_sig = sharemap[cell]->getPort(it.first); log(" Redirecting output %s: %s = %s\n", it.first.c_str(), log_signal(it.second), log_signal(other_sig)); @@ -270,7 +315,9 @@ struct OptShareWorker } } log(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); +#ifdef USE_CELL_HASH_CACHE cell_hash_cache.erase(cell); +#endif module->remove(cell); total_count++; } else { @@ -281,13 +328,13 @@ struct OptShareWorker } }; -struct OptSharePass : public Pass { - OptSharePass() : Pass("opt_share", "consolidate identical cells") { } +struct OptMergePass : public Pass { + OptMergePass() : Pass("opt_merge", "consolidate identical cells") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" opt_share [-nomux] [selection]\n"); + log(" opt_merge [options] [selection]\n"); log("\n"); log("This pass identifies cells with identical type and input signals. Such cells\n"); log("are then merged to one cell.\n"); @@ -295,12 +342,16 @@ struct OptSharePass : public Pass { log(" -nomux\n"); log(" Do not merge MUX cells.\n"); log("\n"); + log(" -share_all\n"); + log(" Operate on all cell types, not just built-in types.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing OPT_SHARE pass (detect identical cells).\n"); + log_header(design, "Executing OPT_MERGE pass (detect identical cells).\n"); bool mode_nomux = false; + bool mode_share_all = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -309,13 +360,17 @@ struct OptSharePass : public Pass { mode_nomux = true; continue; } + if (arg == "-share_all") { + mode_share_all = true; + continue; + } break; } extra_args(args, argidx, design); int total_count = 0; for (auto module : design->selected_modules()) { - OptShareWorker worker(design, module, mode_nomux); + OptMergeWorker worker(design, module, mode_nomux, mode_share_all); total_count += worker.total_count; } @@ -323,6 +378,6 @@ struct OptSharePass : public Pass { design->scratchpad_set_bool("opt.did_something", true); log("Removed a total of %d cells.\n", total_count); } -} OptSharePass; - +} OptMergePass; + PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 982870745..f5ddc2af9 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -2,11 +2,11 @@ * 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 @@ -68,7 +68,7 @@ struct OptMuxtreeWorker OptMuxtreeWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module), assign_map(module), removed_count(0) { - log("Running muxtree optimizier on module %s..\n", module->name.c_str()); + log("Running muxtree optimizer on module %s..\n", module->name.c_str()); log(" Creating internal representation of mux trees.\n"); @@ -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; } @@ -464,7 +464,7 @@ struct OptMuxtreePass : public Pass { } virtual void execute(vector<std::string> args, RTLIL::Design *design) { - log_header("Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n"); + log_header(design, "Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n"); extra_args(args, 1, design); int total_count = 0; @@ -479,5 +479,5 @@ struct OptMuxtreePass : public Pass { log("Removed %d multiplexer ports.\n", total_count); } } OptMuxtreePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc index 5c36eb26b..eb9d02ad5 100644 --- a/passes/opt/opt_reduce.cc +++ b/passes/opt/opt_reduce.cc @@ -2,11 +2,11 @@ * 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 @@ -284,7 +284,7 @@ struct OptReduceWorker did_something = false; // merge trees of reduce_* cells to one single cell and unify input vectors - // (only handle recduce_and and reduce_or for various reasons) + // (only handle reduce_and and reduce_or for various reasons) const char *type_list[] = { "$reduce_or", "$reduce_and" }; for (auto type : type_list) @@ -354,7 +354,7 @@ struct OptReducePass : public Pass { { bool do_fine = false; - log_header("Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).\n"); + log_header(design, "Executing OPT_REDUCE pass (consolidate $*mux and $reduce_* inputs).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -384,5 +384,5 @@ struct OptReducePass : public Pass { log("Performed a total of %d changes.\n", total_count); } } OptReducePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc index 5f52bb8d8..1711d0f45 100644 --- a/passes/opt/opt_rmdff.cc +++ b/passes/opt/opt_rmdff.cc @@ -2,11 +2,11 @@ * 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 @@ -28,6 +28,43 @@ PRIVATE_NAMESPACE_BEGIN SigMap assign_map, dff_init_map; SigSet<RTLIL::Cell*> mux_drivers; +dict<SigBit, pool<SigBit>> init_attributes; + +void remove_init_attr(SigSpec sig) +{ + for (auto bit : assign_map(sig)) + if (init_attributes.count(bit)) + for (auto wbit : init_attributes.at(bit)) + wbit.wire->attributes.at("\\init")[wbit.offset] = State::Sx; +} + +bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch) +{ + SigSpec sig_e = dlatch->getPort("\\EN"); + + if (sig_e == State::S0) + { + RTLIL::Const val_init; + for (auto bit : dff_init_map(dlatch->getPort("\\Q"))) + val_init.bits.push_back(bit.wire == NULL ? bit.data : State::Sx); + mod->connect(dlatch->getPort("\\Q"), val_init); + goto delete_dlatch; + } + + if (sig_e == State::S1) + { + mod->connect(dlatch->getPort("\\Q"), dlatch->getPort("\\D")); + goto delete_dlatch; + } + + return false; + +delete_dlatch: + log("Removing %s (%s) from module %s.\n", dlatch->name.c_str(), dlatch->type.c_str(), mod->name.c_str()); + remove_init_attr(dlatch->getPort("\\Q")); + mod->remove(dlatch); + return true; +} bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) { @@ -83,60 +120,50 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) val_init.bits.push_back(bit.wire == NULL ? bit.data : RTLIL::State::Sx); } - if (dff->type == "$dff" && mux_drivers.has(sig_d) && !has_init) { + if (dff->type == "$dff" && mux_drivers.has(sig_d)) { std::set<RTLIL::Cell*> muxes; mux_drivers.find(sig_d, muxes); for (auto mux : muxes) { RTLIL::SigSpec sig_a = assign_map(mux->getPort("\\A")); RTLIL::SigSpec sig_b = assign_map(mux->getPort("\\B")); - if (sig_a == sig_q && sig_b.is_fully_const()) { - RTLIL::SigSig conn(sig_q, sig_b); - mod->connect(conn); + if (sig_a == sig_q && sig_b.is_fully_const() && (!has_init || val_init == sig_b.as_const())) { + mod->connect(sig_q, sig_b); goto delete_dff; } - if (sig_b == sig_q && sig_a.is_fully_const()) { - RTLIL::SigSig conn(sig_q, sig_a); - mod->connect(conn); + if (sig_b == sig_q && sig_a.is_fully_const() && (!has_init || val_init == sig_a.as_const())) { + mod->connect(sig_q, sig_a); goto delete_dff; } } } - if (sig_c.is_fully_const() && (!sig_r.size() || !has_init)) { + if (sig_c.is_fully_const() && (!sig_r.size() || !has_init || val_init == val_rv)) { if (val_rv.bits.size() == 0) val_rv = val_init; - RTLIL::SigSig conn(sig_q, val_rv); - mod->connect(conn); + mod->connect(sig_q, val_rv); goto delete_dff; } - if (sig_d.is_fully_undef() && sig_r.size() && !has_init) { - RTLIL::SigSig conn(sig_q, val_rv); - mod->connect(conn); + if (sig_d.is_fully_undef() && sig_r.size() && (!has_init || val_init == val_rv)) { + mod->connect(sig_q, val_rv); goto delete_dff; } if (sig_d.is_fully_undef() && !sig_r.size() && has_init) { - RTLIL::SigSig conn(sig_q, val_init); - mod->connect(conn); + mod->connect(sig_q, val_init); goto delete_dff; } - if (sig_d.is_fully_const() && !sig_r.size() && !has_init) { - RTLIL::SigSig conn(sig_q, sig_d); - mod->connect(conn); + if (sig_d.is_fully_const() && (!sig_r.size() || val_rv == sig_d.as_const()) && (!has_init || val_init == sig_d.as_const())) { + mod->connect(sig_q, sig_d); goto delete_dff; } - if (sig_d == sig_q && !(sig_r.size() && has_init)) { - if (sig_r.size()) { - RTLIL::SigSig conn(sig_q, val_rv); - mod->connect(conn); - } - if (has_init) { - RTLIL::SigSig conn(sig_q, val_init); - mod->connect(conn); - } + if (sig_d == sig_q && (!sig_r.size() || !has_init || val_init == val_rv)) { + if (sig_r.size()) + mod->connect(sig_q, val_rv); + if (has_init) + mod->connect(sig_q, val_init); goto delete_dff; } @@ -144,6 +171,7 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) delete_dff: log("Removing %s (%s) from module %s.\n", dff->name.c_str(), dff->type.c_str(), mod->name.c_str()); + remove_init_attr(dff->getPort("\\Q")); mod->remove(dff); return true; } @@ -163,7 +191,7 @@ struct OptRmdffPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { int total_count = 0; - log_header("Executing OPT_RMDFF pass (remove dff with constant values).\n"); + log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n"); extra_args(args, 1, design); @@ -175,11 +203,18 @@ struct OptRmdffPass : public Pass { assign_map.set(mod_it.second); dff_init_map.set(mod_it.second); for (auto &it : mod_it.second->wires_) - if (it.second->attributes.count("\\init") != 0) + if (it.second->attributes.count("\\init") != 0) { dff_init_map.add(it.second, it.second->attributes.at("\\init")); + for (int i = 0; i < GetSize(it.second); i++) { + SigBit wire_bit(it.second, i), mapped_bit = assign_map(wire_bit); + if (mapped_bit.wire) + init_attributes[mapped_bit].insert(wire_bit); + } + } mux_drivers.clear(); std::vector<RTLIL::IdString> dff_list; + std::vector<RTLIL::IdString> dlatch_list; for (auto &it : mod_it.second->cells_) { if (it.second->type == "$mux" || it.second->type == "$pmux") { if (it.second->getPort("\\A").size() == it.second->getPort("\\B").size()) @@ -200,6 +235,7 @@ struct OptRmdffPass : public Pass { if (it.second->type == "$_DFF_PP1_") dff_list.push_back(it.first); if (it.second->type == "$dff") dff_list.push_back(it.first); if (it.second->type == "$adff") dff_list.push_back(it.first); + if (it.second->type == "$dlatch") dlatch_list.push_back(it.first); } for (auto &id : dff_list) { @@ -207,6 +243,12 @@ struct OptRmdffPass : public Pass { handle_dff(mod_it.second, mod_it.second->cells_[id])) total_count++; } + + for (auto &id : dlatch_list) { + if (mod_it.second->cells_.count(id) > 0 && + handle_dlatch(mod_it.second, mod_it.second->cells_[id])) + total_count++; + } } assign_map.clear(); @@ -217,5 +259,5 @@ struct OptRmdffPass : public Pass { log("Replaced %d DFF cells.\n", total_count); } } OptRmdffPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/opt/share.cc b/passes/opt/share.cc index 3133cb2a6..22914eaa7 100644 --- a/passes/opt/share.cc +++ b/passes/opt/share.cc @@ -2,11 +2,11 @@ * 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 @@ -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); @@ -369,7 +369,9 @@ struct ShareWorker } if (cell->type == "$memrd") { - if (!cell->parameters.at("\\CLK_ENABLE").as_bool()) + if (cell->parameters.at("\\CLK_ENABLE").as_bool()) + continue; + if (config.opt_aggressive || !modwalker.sigmap(cell->getPort("\\ADDR")).is_fully_const()) shareable_cells.insert(cell); continue; } @@ -387,7 +389,7 @@ struct ShareWorker } if (generic_ops.count(cell->type)) { - if (config.opt_aggressive || cell->parameters.at("\\Y_WIDTH").as_int() >= 10) + if (config.opt_aggressive) shareable_cells.insert(cell); continue; } @@ -417,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; @@ -436,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; @@ -456,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; @@ -473,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; } @@ -530,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); @@ -561,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) { @@ -628,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); @@ -706,6 +708,10 @@ struct ShareWorker if (c1->type == "$memrd") { RTLIL::Cell *supercell = module->addCell(NEW_ID, c1); + RTLIL::SigSpec addr1 = c1->getPort("\\ADDR"); + RTLIL::SigSpec addr2 = c2->getPort("\\ADDR"); + if (addr1 != addr2) + supercell->setPort("\\ADDR", module->Mux(NEW_ID, addr2, addr1, act)); supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort("\\DATA"), c2->getPort("\\DATA"))); supercell_aux.insert(supercell); return supercell; @@ -787,10 +793,59 @@ struct ShareWorker return true; } - void optimize_activation_patterns(pool<ssc_pair_t> & /* patterns */) + void optimize_activation_patterns(pool<ssc_pair_t> &patterns) { // TODO: Remove patterns that are contained in other patterns - // TODO: Consolidate pairs of patterns that only differ in the value for one signal bit + + dict<SigSpec, pool<Const>> db; + bool did_something = false; + + for (auto const &p : patterns) + { + auto &sig = p.first; + auto &val = p.second; + int len = GetSize(sig); + + for (int i = 0; i < len; i++) + { + auto otherval = val; + + if (otherval.bits[i] == State::S0) + otherval.bits[i] = State::S1; + else if (otherval.bits[i] == State::S1) + otherval.bits[i] = State::S0; + else + continue; + + if (db[sig].count(otherval)) + { + auto newsig = sig; + newsig.remove(i); + + auto newval = val; + newval.bits.erase(newval.bits.begin() + i); + + db[newsig].insert(newval); + db[sig].erase(otherval); + + did_something = true; + goto next_pattern; + } + } + + db[sig].insert(val); + next_pattern:; + } + + if (!did_something) + return; + + patterns.clear(); + for (auto &it : db) + for (auto &val : it.second) + patterns.insert(make_pair(it.first, val)); + + optimize_activation_patterns(patterns); } const pool<ssc_pair_t> &find_cell_activation_patterns(RTLIL::Cell *cell, const char *indent) @@ -1105,7 +1160,7 @@ struct ShareWorker RTLIL::Cell *cell = *shareable_cells.begin(); shareable_cells.erase(cell); - log(" Analyzing resource sharing options for %s:\n", log_id(cell)); + log(" Analyzing resource sharing options for %s (%s):\n", log_id(cell), log_id(cell->type)); const pool<ssc_pair_t> &cell_activation_patterns = find_cell_activation_patterns(cell, " "); RTLIL::SigSpec cell_activation_signals = bits_from_activation_patterns(cell_activation_patterns); @@ -1138,7 +1193,7 @@ struct ShareWorker for (auto other_cell : candidates) { - log(" Analyzing resource sharing with %s:\n", log_id(other_cell)); + log(" Analyzing resource sharing with %s (%s):\n", log_id(other_cell), log_id(other_cell->type)); const pool<ssc_pair_t> &other_cell_activation_patterns = find_cell_activation_patterns(other_cell, " "); RTLIL::SigSpec other_cell_activation_signals = bits_from_activation_patterns(other_cell_activation_patterns); @@ -1178,8 +1233,8 @@ struct ShareWorker optimize_activation_patterns(filtered_cell_activation_patterns); optimize_activation_patterns(filtered_other_cell_activation_patterns); - ezDefaultSAT ez; - SatGen satgen(&ez, &modwalker.sigmap); + ezSatPtr ez; + SatGen satgen(ez.get(), &modwalker.sigmap); pool<RTLIL::Cell*> sat_cells; std::set<RTLIL::SigBit> bits_queue; @@ -1189,13 +1244,13 @@ struct ShareWorker for (auto &p : filtered_cell_activation_patterns) { log(" Activation pattern for cell %s: %s = %s\n", log_id(cell), log_signal(p.first), log_signal(p.second)); - cell_active.push_back(ez.vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); + cell_active.push_back(ez->vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); all_ctrl_signals.append(p.first); } for (auto &p : filtered_other_cell_activation_patterns) { log(" Activation pattern for cell %s: %s = %s\n", log_id(other_cell), log_signal(p.first), log_signal(p.second)); - other_cell_active.push_back(ez.vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); + other_cell_active.push_back(ez->vec_eq(satgen.importSigSpec(p.first), satgen.importSigSpec(p.second))); all_ctrl_signals.append(p.first); } @@ -1230,36 +1285,36 @@ struct ShareWorker log(" Adding exclusive control bits: %s vs. %s\n", log_signal(it.first), log_signal(it.second)); int sub1 = satgen.importSigBit(it.first); int sub2 = satgen.importSigBit(it.second); - ez.assume(ez.NOT(ez.AND(sub1, sub2))); + ez->assume(ez->NOT(ez->AND(sub1, sub2))); } - if (!ez.solve(ez.expression(ez.OpOr, cell_active))) { + if (!ez->solve(ez->expression(ez->OpOr, cell_active))) { log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(cell)); cells_to_remove.insert(cell); break; } - if (!ez.solve(ez.expression(ez.OpOr, other_cell_active))) { + if (!ez->solve(ez->expression(ez->OpOr, other_cell_active))) { log(" According to the SAT solver the cell %s is never active. Sharing is pointless, we simply remove it.\n", log_id(other_cell)); cells_to_remove.insert(other_cell); shareable_cells.erase(other_cell); continue; } - ez.non_incremental(); + ez->non_incremental(); all_ctrl_signals.sort_and_unify(); std::vector<int> sat_model = satgen.importSigSpec(all_ctrl_signals); std::vector<bool> sat_model_values; - int sub1 = ez.expression(ez.OpOr, cell_active); - int sub2 = ez.expression(ez.OpOr, other_cell_active); - ez.assume(ez.AND(sub1, sub2)); + int sub1 = ez->expression(ez->OpOr, cell_active); + int sub2 = ez->expression(ez->OpOr, other_cell_active); + ez->assume(ez->AND(sub1, sub2)); log(" Size of SAT problem: %d cells, %d variables, %d clauses\n", - GetSize(sat_cells), ez.numCnfVariables(), ez.numCnfClauses()); + GetSize(sat_cells), ez->numCnfVariables(), ez->numCnfClauses()); - if (ez.solve(sat_model, sat_model_values)) { + if (ez->solve(sat_model, sat_model_values)) { log(" According to the SAT solver this pair of cells can not be shared.\n"); log(" Model from SAT solver: %s = %d'", log_signal(all_ctrl_signals), GetSize(sat_model_values)); for (int i = GetSize(sat_model_values)-1; i >= 0; i--) @@ -1391,7 +1446,7 @@ struct SharePass : public Pass { log("\n"); log(" -fast\n"); log(" Only consider the simple part of the control logic in SAT solving, resulting\n"); - log(" in much easier SAT problems at the cost of maybe missing some oportunities\n"); + log(" in much easier SAT problems at the cost of maybe missing some opportunities\n"); log(" for resource sharing.\n"); log("\n"); log(" -limit N\n"); @@ -1445,7 +1500,7 @@ struct SharePass : public Pass { config.generic_other_ops.insert("$alu"); config.generic_other_ops.insert("$macc"); - log_header("Executing SHARE pass (SAT-based resource sharing).\n"); + log_header(design, "Executing SHARE pass (SAT-based resource sharing).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 1609a8be7..333541eab 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -2,11 +2,11 @@ * 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 @@ -37,7 +37,7 @@ struct WreduceConfig "$and", "$or", "$xor", "$xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", - "$add", "$sub", // "$mul", "$div", "$mod", "$pow", + "$add", "$sub", "$mul", // "$div", "$mod", "$pow", "$mux", "$pmux" }); } @@ -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; + } + } - // Reduce size of port Y based on sizes for A and B and unused bits in Y + 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; + } + } - 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); @@ -281,6 +318,10 @@ struct WreduceWorker work_queue_cells.insert(port.cell); } + pool<SigSpec> complete_wires; + for (auto w : module->wires()) + complete_wires.insert(mi.sigmap(w)); + for (auto w : module->selected_wires()) { int unused_top_bits = 0; @@ -296,19 +337,22 @@ struct WreduceWorker unused_top_bits++; } - if (0 < unused_top_bits && unused_top_bits < GetSize(w)) { - log("Removed top %d bits (of %d) from wire %s.%s.\n", unused_top_bits, GetSize(w), log_id(module), log_id(w)); - Wire *nw = module->addWire(NEW_ID, w); - nw->width = GetSize(w) - unused_top_bits; - module->connect(nw, SigSpec(w).extract(0, GetSize(nw))); - module->swap_names(w, nw); - } + if (unused_top_bits == 0 || unused_top_bits == GetSize(w)) + continue; + + if (complete_wires[mi.sigmap(w).extract(0, GetSize(w) - unused_top_bits)]) + continue; + + log("Removed top %d bits (of %d) from wire %s.%s.\n", unused_top_bits, GetSize(w), log_id(module), log_id(w)); + Wire *nw = module->addWire(NEW_ID, GetSize(w) - unused_top_bits); + module->connect(nw, SigSpec(w).extract(0, GetSize(nw))); + module->swap_names(w, nw); } } }; struct WreducePass : public Pass { - WreducePass() : Pass("wreduce", "reduce the word size of operations is possible") { } + WreducePass() : Pass("wreduce", "reduce the word size of operations if possible") { } virtual void help() { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| @@ -327,7 +371,7 @@ struct WreducePass : public Pass { { WreduceConfig config; - log_header("Executing WREDUCE pass (reducing word size of cells).\n"); + log_header(design, "Executing WREDUCE pass (reducing word size of cells).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -340,6 +384,19 @@ struct WreducePass : public Pass { if (module->has_processes_warn()) continue; + for (auto c : module->selected_cells()) + if (c->type.in("$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool", + "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", + "$logic_not", "$logic_and", "$logic_or") && GetSize(c->getPort("\\Y")) > 1) { + SigSpec sig = c->getPort("\\Y"); + 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); worker.run(); } diff --git a/passes/proc/Makefile.inc b/passes/proc/Makefile.inc index dfbc78eae..397fe46a1 100644 --- a/passes/proc/Makefile.inc +++ b/passes/proc/Makefile.inc @@ -5,5 +5,6 @@ OBJS += passes/proc/proc_rmdead.o OBJS += passes/proc/proc_init.o OBJS += passes/proc/proc_arst.o OBJS += passes/proc/proc_mux.o +OBJS += passes/proc/proc_dlatch.o OBJS += passes/proc/proc_dff.o diff --git a/passes/proc/proc.cc b/passes/proc/proc.cc index 30efbab4b..d5366f266 100644 --- a/passes/proc/proc.cc +++ b/passes/proc/proc.cc @@ -2,11 +2,11 @@ * 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 @@ -40,22 +40,29 @@ struct ProcPass : public Pass { log(" proc_init\n"); log(" proc_arst\n"); log(" proc_mux\n"); + log(" proc_dlatch\n"); log(" proc_dff\n"); log(" proc_clean\n"); log("\n"); - log("This replaces the processes in the design with multiplexers and flip-flops.\n"); + log("This replaces the processes in the design with multiplexers,\n"); + log("flip-flops and latches.\n"); log("\n"); log("The following options are supported:\n"); log("\n"); log(" -global_arst [!]<netname>\n"); log(" This option is passed through to proc_arst.\n"); log("\n"); + log(" -ifx\n"); + log(" This option is passed through to proc_mux. proc_rmdead is not\n"); + log(" executed in -ifx mode.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { std::string global_arst; + bool ifxmode = false; - log_header("Executing PROC pass (convert processes to netlists).\n"); + log_header(design, "Executing PROC pass (convert processes to netlists).\n"); log_push(); size_t argidx; @@ -65,23 +72,29 @@ struct ProcPass : public Pass { global_arst = args[++argidx]; continue; } + if (args[argidx] == "-ifx") { + ifxmode = true; + continue; + } break; } extra_args(args, argidx, design); Pass::call(design, "proc_clean"); - Pass::call(design, "proc_rmdead"); + if (!ifxmode) + Pass::call(design, "proc_rmdead"); Pass::call(design, "proc_init"); if (global_arst.empty()) Pass::call(design, "proc_arst"); else Pass::call(design, "proc_arst -global_arst " + global_arst); - Pass::call(design, "proc_mux"); + Pass::call(design, ifxmode ? "proc_mux -ifx" : "proc_mux"); + Pass::call(design, "proc_dlatch"); Pass::call(design, "proc_dff"); Pass::call(design, "proc_clean"); log_pop(); } } ProcPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_arst.cc b/passes/proc/proc_arst.cc index 27c6b3bcf..216b00ddd 100644 --- a/passes/proc/proc_arst.cc +++ b/passes/proc/proc_arst.cc @@ -2,11 +2,11 @@ * 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 @@ -226,7 +226,7 @@ struct ProcArstPass : public Pass { std::string global_arst; bool global_arst_neg = false; - log_header("Executing PROC_ARST pass (detect async resets in processes).\n"); + log_header(design, "Executing PROC_ARST pass (detect async resets in processes).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -244,6 +244,7 @@ struct ProcArstPass : public Pass { } extra_args(args, argidx, design); + pool<Wire*> delete_initattr_wires; for (auto mod : design->modules()) if (design->selected(mod)) { @@ -265,6 +266,7 @@ struct ProcArstPass : public Pass { value.extend_u0(chunk.wire->width, false); arst_sig.append(chunk); arst_val.append(value.extract(chunk.offset, chunk.width)); + delete_initattr_wires.insert(chunk.wire); } if (arst_sig.size()) { log("Added global reset to process %s: %s <- %s\n", @@ -281,7 +283,10 @@ struct ProcArstPass : public Pass { } } } + + for (auto wire : delete_initattr_wires) + wire->attributes.erase("\\init"); } } ProcArstPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc index 82716cd06..7dbabc211 100644 --- a/passes/proc/proc_clean.cc +++ b/passes/proc/proc_clean.cc @@ -2,11 +2,11 @@ * 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 @@ -156,7 +156,7 @@ struct ProcCleanPass : public Pass { virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { int total_count = 0; - log_header("Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); + log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); extra_args(args, 1, design); @@ -183,5 +183,5 @@ struct ProcCleanPass : public Pass { log("Cleaned up %d empty switch%s.\n", total_count, total_count == 1 ? "" : "es"); } } ProcCleanPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_dff.cc b/passes/proc/proc_dff.cc index 76842da6b..f532990c2 100644 --- a/passes/proc/proc_dff.cc +++ b/passes/proc/proc_dff.cc @@ -2,11 +2,11 @@ * 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 @@ -369,7 +369,7 @@ struct ProcDffPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_DFF pass (convert process syncs to FFs).\n"); + log_header(design, "Executing PROC_DFF pass (convert process syncs to FFs).\n"); extra_args(args, 1, design); @@ -382,5 +382,5 @@ struct ProcDffPass : public Pass { } } } ProcDffPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc new file mode 100644 index 000000000..6621afd33 --- /dev/null +++ b/passes/proc/proc_dlatch.cc @@ -0,0 +1,449 @@ +/* + * 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/sigtools.h" +#include "kernel/consteval.h" +#include "kernel/log.h" +#include <sstream> +#include <stdlib.h> +#include <stdio.h> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct proc_dlatch_db_t +{ + Module *module; + SigMap sigmap; + + pool<Cell*> generated_dlatches; + dict<Cell*, vector<SigBit>> mux_srcbits; + dict<SigBit, pair<Cell*, int>> mux_drivers; + dict<SigBit, int> sigusers; + + proc_dlatch_db_t(Module *module) : module(module), sigmap(module) + { + for (auto cell : module->cells()) + { + if (cell->type.in("$mux", "$pmux")) + { + auto sig_y = sigmap(cell->getPort("\\Y")); + for (int i = 0; i < GetSize(sig_y); i++) + mux_drivers[sig_y[i]] = pair<Cell*, int>(cell, i); + + pool<SigBit> mux_srcbits_pool; + for (auto bit : sigmap(cell->getPort("\\A"))) + mux_srcbits_pool.insert(bit); + for (auto bit : sigmap(cell->getPort("\\B"))) + mux_srcbits_pool.insert(bit); + + vector<SigBit> mux_srcbits_vec; + for (auto bit : mux_srcbits_pool) + if (bit.wire != nullptr) + mux_srcbits_vec.push_back(bit); + + mux_srcbits[cell].swap(mux_srcbits_vec); + } + + for (auto &conn : cell->connections()) + if (!cell->known() || cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + sigusers[bit]++; + } + + for (auto wire : module->wires()) + if (wire->port_input) + for (auto bit : sigmap(wire)) + sigusers[bit]++; + } + + bool quickcheck(const SigSpec &haystack, const SigSpec &needle) + { + pool<SigBit> haystack_bits = sigmap(haystack).to_sigbit_pool(); + pool<SigBit> needle_bits = sigmap(needle).to_sigbit_pool(); + + pool<Cell*> cells_queue, cells_visited; + pool<SigBit> bits_queue, bits_visited; + + bits_queue = haystack_bits; + while (!bits_queue.empty()) + { + for (auto &bit : bits_queue) { + auto it = mux_drivers.find(bit); + if (it != mux_drivers.end()) + if (!cells_visited.count(it->second.first)) + cells_queue.insert(it->second.first); + bits_visited.insert(bit); + } + + bits_queue.clear(); + + for (auto c : cells_queue) { + for (auto bit : mux_srcbits[c]) { + if (needle_bits.count(bit)) + return true; + if (!bits_visited.count(bit)) + bits_queue.insert(bit); + } + } + + cells_queue.clear(); + } + + return false; + } + + struct rule_node_t + { + // a node is true if "signal" equals "match" and [any + // of the child nodes is true or "children" is empty] + SigBit signal, match; + vector<int> children; + + bool operator==(const rule_node_t &other) const { + return signal == other.signal && match == other.match && children == other.children; + } + + unsigned int hash() const { + unsigned int h = mkhash_init; + mkhash(h, signal.hash()); + mkhash(h, match.hash()); + for (auto i : children) mkhash(h, i); + return h; + } + }; + + enum tf_node_types_t : int { + true_node = 1, + false_node = 2 + }; + + idict<rule_node_t, 3> rules_db; + dict<int, SigBit> rules_sig; + + int make_leaf(SigBit signal, SigBit match) + { + rule_node_t node; + node.signal = signal; + node.match = match; + return rules_db(node); + } + + int make_inner(SigBit signal, SigBit match, int child) + { + rule_node_t node; + node.signal = signal; + node.match = match; + node.children.push_back(child); + return rules_db(node); + } + + int make_inner(const pool<int> &children) + { + rule_node_t node; + node.signal = State::S0; + node.match = State::S0; + node.children = vector<int>(children.begin(), children.end()); + std::sort(node.children.begin(), node.children.end()); + return rules_db(node); + } + + int find_mux_feedback(SigBit haystack, SigBit needle, bool set_undef) + { + if (sigusers[haystack] > 1) + set_undef = false; + + if (haystack == needle) + return true_node; + + auto it = mux_drivers.find(haystack); + if (it == mux_drivers.end()) + return false_node; + + Cell *cell = it->second.first; + int index = it->second.second; + + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + SigSpec sig_s = sigmap(cell->getPort("\\S")); + int width = GetSize(sig_a); + + pool<int> children; + + int n = find_mux_feedback(sig_a[index], needle, set_undef); + if (n != false_node) { + if (set_undef && sig_a[index] == needle) { + SigSpec sig = cell->getPort("\\A"); + sig[index] = State::Sx; + cell->setPort("\\A", sig); + } + for (int i = 0; i < GetSize(sig_s); i++) + n = make_inner(sig_s[i], State::S0, n); + children.insert(n); + } + + for (int i = 0; i < GetSize(sig_s); i++) { + n = find_mux_feedback(sig_b[i*width + index], needle, set_undef); + if (n != false_node) { + if (set_undef && sig_b[i*width + index] == needle) { + SigSpec sig = cell->getPort("\\B"); + sig[i*width + index] = State::Sx; + cell->setPort("\\B", sig); + } + children.insert(make_inner(sig_s[i], State::S1, n)); + } + } + + if (children.empty()) + return false_node; + + return make_inner(children); + } + + SigBit make_hold(int n) + { + if (n == true_node) + return State::S1; + + if (n == false_node) + return State::S0; + + if (rules_sig.count(n)) + return rules_sig.at(n); + + const rule_node_t &rule = rules_db[n]; + SigSpec and_bits; + + if (rule.signal != rule.match) { + if (rule.match == State::S1) + and_bits.append(rule.signal); + else if (rule.match == State::S0) + and_bits.append(module->Not(NEW_ID, rule.signal)); + else + and_bits.append(module->Eq(NEW_ID, rule.signal, rule.match)); + } + + if (!rule.children.empty()) { + SigSpec or_bits; + for (int k : rule.children) + or_bits.append(make_hold(k)); + and_bits.append(module->ReduceOr(NEW_ID, or_bits)); + } + + if (GetSize(and_bits) == 2) + and_bits = module->And(NEW_ID, and_bits[0], and_bits[1]); + log_assert(GetSize(and_bits) == 1); + + rules_sig[n] = and_bits[0]; + return and_bits[0]; + } + + void fixup_mux(Cell *cell) + { + SigSpec sig_a = cell->getPort("\\A"); + SigSpec sig_b = cell->getPort("\\B"); + SigSpec sig_s = cell->getPort("\\S"); + SigSpec sig_any_valid_b; + + SigSpec sig_new_b, sig_new_s; + for (int i = 0; i < GetSize(sig_s); i++) { + SigSpec b = sig_b.extract(i*GetSize(sig_a), GetSize(sig_a)); + if (!b.is_fully_undef()) { + sig_any_valid_b = b; + sig_new_b.append(b); + sig_new_s.append(sig_s[i]); + } + } + + if (sig_new_s.empty()) { + sig_new_b = sig_a; + sig_new_s = State::S0; + } + + if (sig_a.is_fully_undef() && !sig_any_valid_b.empty()) + cell->setPort("\\A", sig_any_valid_b); + + if (GetSize(sig_new_s) == 1) { + cell->type = "$mux"; + cell->unsetParam("\\S_WIDTH"); + } else { + cell->type = "$pmux"; + cell->setParam("\\S_WIDTH", GetSize(sig_new_s)); + } + + cell->setPort("\\B", sig_new_b); + cell->setPort("\\S", sig_new_s); + } + + void fixup_muxes() + { + pool<Cell*> visited, queue; + dict<Cell*, pool<SigBit>> upstream_cell2net; + dict<SigBit, pool<Cell*>> upstream_net2cell; + + CellTypes ct; + ct.setup_internals(); + + for (auto cell : module->cells()) + for (auto conn : cell->connections()) { + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + upstream_cell2net[cell].insert(bit); + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + upstream_net2cell[bit].insert(cell); + } + + queue = generated_dlatches; + while (!queue.empty()) + { + pool<Cell*> next_queue; + + for (auto cell : queue) { + if (cell->type.in("$mux", "$pmux")) + fixup_mux(cell); + for (auto bit : upstream_cell2net[cell]) + for (auto cell : upstream_net2cell[bit]) + next_queue.insert(cell); + visited.insert(cell); + } + + queue.clear(); + for (auto cell : next_queue) { + if (!visited.count(cell) && ct.cell_known(cell->type)) + queue.insert(cell); + } + } + } +}; + +void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) +{ + std::vector<RTLIL::SyncRule*> new_syncs; + RTLIL::SigSig latches_bits, nolatches_bits; + dict<SigBit, SigBit> latches_out_in; + dict<SigBit, int> latches_hold; + + for (auto sr : proc->syncs) + { + if (sr->type != RTLIL::SyncType::STa) { + new_syncs.push_back(sr); + continue; + } + + for (auto ss : sr->actions) + { + db.sigmap.apply(ss.first); + db.sigmap.apply(ss.second); + + if (!db.quickcheck(ss.second, ss.first)) { + nolatches_bits.first.append(ss.first); + nolatches_bits.second.append(ss.second); + continue; + } + + for (int i = 0; i < GetSize(ss.first); i++) + latches_out_in[ss.first[i]] = ss.second[i]; + } + + delete sr; + } + + latches_out_in.sort(); + for (auto &it : latches_out_in) { + int n = db.find_mux_feedback(it.second, it.first, true); + if (n == db.false_node) { + nolatches_bits.first.append(it.first); + nolatches_bits.second.append(it.second); + } else { + latches_bits.first.append(it.first); + latches_bits.second.append(it.second); + latches_hold[it.first] = n; + } + } + + int offset = 0; + for (auto chunk : nolatches_bits.first.chunks()) { + SigSpec lhs = chunk, rhs = nolatches_bits.second.extract(offset, chunk.width); + log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n", + db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str()); + db.module->connect(lhs, rhs); + offset += chunk.width; + } + + offset = 0; + while (offset < GetSize(latches_bits.first)) + { + int width = 1; + int n = latches_hold[latches_bits.first[offset]]; + Wire *w = latches_bits.first[offset].wire; + + if (w != nullptr) + { + while (offset+width < GetSize(latches_bits.first) && + n == latches_hold[latches_bits.first[offset+width]] && + w == latches_bits.first[offset+width].wire) + width++; + + SigSpec lhs = latches_bits.first.extract(offset, width); + SigSpec rhs = latches_bits.second.extract(offset, width); + + Cell *cell = db.module->addDlatch(NEW_ID, db.module->Not(NEW_ID, db.make_hold(n)), rhs, lhs); + db.generated_dlatches.insert(cell); + + log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n", + db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str(), log_id(cell)); + } + + offset += width; + } + + new_syncs.swap(proc->syncs); +} + +struct ProcDlatchPass : public Pass { + ProcDlatchPass() : Pass("proc_dlatch", "extract latches from processes") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" proc_dlatch [selection]\n"); + log("\n"); + log("This pass identifies latches in the processes and converts them to\n"); + log("d-type latches.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing PROC_DLATCH pass (convert process syncs to latches).\n"); + + extra_args(args, 1, design); + + for (auto module : design->selected_modules()) { + proc_dlatch_db_t db(module); + for (auto &proc_it : module->processes) + if (design->selected(module, proc_it.second)) + proc_dlatch(db, proc_it.second); + db.fixup_muxes(); + } + } +} ProcDlatchPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_init.cc b/passes/proc/proc_init.cc index dff68159f..0c8fb83dc 100644 --- a/passes/proc/proc_init.cc +++ b/passes/proc/proc_init.cc @@ -2,11 +2,11 @@ * 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 @@ -61,13 +61,28 @@ void proc_init(RTLIL::Module *mod, RTLIL::Process *proc) log_cmd_error("Failed to get a constant init value for %s: %s\n", log_signal(lhs), log_signal(rhs)); int offset = 0; - for (auto &lhs_c : lhs.chunks()) { - if (lhs_c.wire != NULL) { - RTLIL::SigSpec value = rhs.extract(offset, lhs_c.width); - if (value.size() != lhs_c.wire->width) - log_cmd_error("Init value is not for the entire wire: %s = %s\n", log_signal(lhs_c), log_signal(value)); - log(" Setting init value: %s = %s\n", log_signal(lhs_c.wire), log_signal(value)); - lhs_c.wire->attributes["\\init"] = value.as_const(); + for (auto &lhs_c : lhs.chunks()) + { + if (lhs_c.wire != nullptr) + { + SigSpec valuesig = rhs.extract(offset, lhs_c.width); + if (!valuesig.is_fully_const()) + log_cmd_error("Non-const initialization value: %s = %s\n", log_signal(lhs_c), log_signal(valuesig)); + + Const value = valuesig.as_const(); + Const &wireinit = lhs_c.wire->attributes["\\init"]; + + while (GetSize(wireinit.bits) < lhs_c.wire->width) + wireinit.bits.push_back(State::Sx); + + for (int i = 0; i < lhs_c.width; i++) { + auto &initbit = wireinit.bits[i + lhs_c.offset]; + if (initbit != State::Sx && initbit != value[i]) + log_cmd_error("Conflicting initialization values for %s.\n", log_signal(lhs_c)); + initbit = value[i]; + } + + log(" Set init value: %s = %s\n", log_signal(lhs_c.wire), log_signal(wireinit)); } offset += lhs_c.width; } @@ -93,14 +108,14 @@ struct ProcInitPass : public Pass { log("\n"); log(" proc_init [selection]\n"); log("\n"); - log("This pass extracts the 'init' actions from processes (generated from verilog\n"); + log("This pass extracts the 'init' actions from processes (generated from Verilog\n"); log("'initial' blocks) and sets the initial value to the 'init' attribute on the\n"); log("respective wire.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_INIT pass (extract init attributes).\n"); + log_header(design, "Executing PROC_INIT pass (extract init attributes).\n"); extra_args(args, 1, design); @@ -111,5 +126,5 @@ struct ProcInitPass : public Pass { proc_init(mod, proc_it.second); } } ProcInitPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 4aa1aab54..57e131ca5 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -2,11 +2,11 @@ * 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 @@ -27,37 +27,123 @@ 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(); + } + } -RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw) + 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, bool ifxmode) { std::stringstream sstr; sstr << "$procmux$" << (autoidx++); @@ -78,14 +164,14 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s if (comp.size() == 0) return RTLIL::SigSpec(); - if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1)) + if (sig.size() == 1 && comp == RTLIL::SigSpec(1,1) && !ifxmode) { mod->connect(RTLIL::SigSig(RTLIL::SigSpec(cmp_wire, cmp_wire->width++), sig)); } else { // create compare cell - RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), "$eq"); + RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), ifxmode ? "$eqx" : "$eq"); eq_cell->attributes = sw->attributes; eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0); @@ -125,7 +211,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return RTLIL::SigSpec(ctrl_wire); } -RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw) +RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode) { log_assert(when_signal.size() == else_signal.size()); @@ -137,7 +223,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return when_signal; // compare results - RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw); + RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode); if (ctrl_sig.size() == 0) return when_signal; log_assert(ctrl_sig.size() == 1); @@ -159,12 +245,15 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s return RTLIL::SigSpec(result_wire); } -void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw) +void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode) { log_assert(last_mux_cell != NULL); log_assert(when_signal.size() == last_mux_cell->getPort("\\A").size()); - RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw); + if (when_signal == last_mux_cell->getPort("\\A")) + return; + + RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode); log_assert(ctrl_sig.size() == 1); last_mux_cell->type = "$pmux"; @@ -179,7 +268,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, bool ifxmode) { RTLIL::SigSpec result = defval; @@ -190,9 +280,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++) { @@ -214,7 +332,7 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, RTLIL::CaseRule *cs, const for (auto pat : cs2->compare) if (!pat.is_fully_const()) extra_group_for_next_case = true; - else + else if (!ifxmode) pool.take(pat); } } @@ -225,37 +343,39 @@ 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, ifxmode); if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1]) - append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw); + append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, ifxmode); else - result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw); + result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, ifxmode); } } return result; } -void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc) +void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode) { - 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); - extract_core_signal(&proc->root_case, sig); + dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> swpara; - log(" creating decoder for signal `%s'.\n", log_signal(sig)); + int cnt = 0; + for (int idx : sigsnip.snippets) + { + swcache.current_snippet = idx; + RTLIL::SigSpec sig = sigsnip.sigidx[idx]; - RTLIL::SigSpec value = signal_to_mux_tree(mod, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size())); + log("%6d/%d: %s\n", ++cnt, GetSize(sigsnip.snippets), log_signal(sig)); + + RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, &proc->root_case, sig, RTLIL::SigSpec(RTLIL::State::Sx, sig.size()), ifxmode); mod->connect(RTLIL::SigSig(sig, value)); } } @@ -266,24 +386,38 @@ struct ProcMuxPass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" proc_mux [selection]\n"); + log(" proc_mux [options] [selection]\n"); log("\n"); log("This pass converts the decision trees in processes (originating from if-else\n"); log("and case statements) to trees of multiplexer cells.\n"); log("\n"); + log(" -ifx\n"); + log(" Use Verilog simulation behavior with respect to undef values in\n"); + log(" 'case' expressions and 'if' conditions.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_MUX pass (convert decision trees to multiplexers).\n"); + bool ifxmode = false; + log_header(design, "Executing PROC_MUX pass (convert decision trees to multiplexers).\n"); - extra_args(args, 1, design); + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-ifx") { + ifxmode = true; + continue; + } + break; + } + extra_args(args, argidx, design); for (auto mod : design->modules()) if (design->selected(mod)) for (auto &proc_it : mod->processes) if (design->selected(mod, proc_it.second)) - proc_mux(mod, proc_it.second); + proc_mux(mod, proc_it.second, ifxmode); } } ProcMuxPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc index 427e0d567..5672fb475 100644 --- a/passes/proc/proc_rmdead.cc +++ b/passes/proc/proc_rmdead.cc @@ -2,11 +2,11 @@ * 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 @@ -51,8 +51,8 @@ void proc_rmdead(RTLIL::SwitchRule *sw, int &counter) counter++; continue; } - if (pool.empty()) - sw->cases[i]->compare.clear(); + // if (pool.empty()) + // sw->cases[i]->compare.clear(); } for (auto switch_it : sw->cases[i]->switches) @@ -76,7 +76,7 @@ struct ProcRmdeadPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n"); + log_header(design, "Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n"); extra_args(args, 1, design); @@ -100,5 +100,5 @@ struct ProcRmdeadPass : public Pass { log("Removed a total of %d dead cases.\n", total_counter); } } ProcRmdeadPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/sat/eval.cc b/passes/sat/eval.cc index 62534ec0b..09f69cc5c 100644 --- a/passes/sat/eval.cc +++ b/passes/sat/eval.cc @@ -2,11 +2,11 @@ * 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 @@ -87,7 +87,7 @@ struct BruteForceEquivChecker BruteForceEquivChecker(RTLIL::Module *mod1, RTLIL::Module *mod2, bool ignore_x_mod1) : mod1(mod1), mod2(mod2), counter(0), errors(0), ignore_x_mod1(ignore_x_mod1) { - log("Checking for equivialence (brute-force): %s vs %s\n", mod1->name.c_str(), mod2->name.c_str()); + log("Checking for equivalence (brute-force): %s vs %s\n", mod1->name.c_str(), mod2->name.c_str()); for (auto &w : mod1->wires_) { RTLIL::Wire *wire1 = w.second; @@ -143,16 +143,16 @@ struct VlogHammerReporter { log("Verifying SAT model (%s)..\n", model_undef ? "with undef" : "without undef"); - ezDefaultSAT ez; + ezSatPtr ez; SigMap sigmap(module); - SatGen satgen(&ez, &sigmap); + SatGen satgen(ez.get(), &sigmap); satgen.model_undef = model_undef; for (auto &c : module->cells_) if (!satgen.importCell(c.second)) log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(c.first), RTLIL::id2cstr(c.second->type)); - ez.assume(satgen.signals_eq(recorded_set_vars, recorded_set_vals)); + ez->assume(satgen.signals_eq(recorded_set_vars, recorded_set_vals)); std::vector<int> y_vec = satgen.importDefSigSpec(module->wires_.at("\\y")); std::vector<bool> y_values; @@ -163,9 +163,9 @@ struct VlogHammerReporter } log(" Created SAT problem with %d variables and %d clauses.\n", - ez.numCnfVariables(), ez.numCnfClauses()); + ez->numCnfVariables(), ez->numCnfClauses()); - if (!ez.solve(y_vec, y_values)) + if (!ez->solve(y_vec, y_values)) log_error("Failed to find solution to SAT problem.\n"); for (int i = 0; i < expected_y.size(); i++) { @@ -204,7 +204,7 @@ struct VlogHammerReporter if (y_undef.at(i)) { log(" Toggling undef bit %d to test undef gating.\n", i); - if (!ez.solve(y_vec, y_values, ez.IFF(y_vec.at(i), y_values.at(i) ? ez.CONST_FALSE : ez.CONST_TRUE))) + if (!ez->solve(y_vec, y_values, ez->IFF(y_vec.at(i), y_values.at(i) ? ez->CONST_FALSE : ez->CONST_TRUE))) log_error("Failed to find solution with toggled bit!\n"); cmp_vars.push_back(y_vec.at(expected_y.size() + i)); @@ -220,15 +220,15 @@ struct VlogHammerReporter } log(" Testing if SAT solution is unique.\n"); - ez.assume(ez.vec_ne(cmp_vars, ez.vec_const(cmp_vals))); - if (ez.solve(y_vec, y_values)) + ez->assume(ez->vec_ne(cmp_vars, ez->vec_const(cmp_vals))); + if (ez->solve(y_vec, y_values)) log_error("Found two distinct solutions to SAT problem.\n"); } else { log(" Testing if SAT solution is unique.\n"); - ez.assume(ez.vec_ne(y_vec, ez.vec_const(y_values))); - if (ez.solve(y_vec, y_values)) + ez->assume(ez->vec_ne(y_vec, ez->vec_const(y_values))); + if (ez->solve(y_vec, y_values)) log_error("Found two distinct solutions to SAT problem.\n"); } @@ -389,7 +389,7 @@ struct EvalPass : public Pass { std::vector<std::string> shows, tables; bool set_undef = false; - log_header("Executing EVAL pass (evaluate the circuit given an input).\n"); + log_header(design, "Executing EVAL pass (evaluate the circuit given an input).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -421,7 +421,7 @@ struct EvalPass : public Pass { log_error("Can't find module `%s'!\n", mod2_name.c_str()); BruteForceEquivChecker checker(design->modules_.at(mod1_name), design->modules_.at(mod2_name), args[argidx-2] == "-brute_force_equiv_checker_x"); if (checker.errors > 0) - log_cmd_error("Modules are not equivialent!\n"); + log_cmd_error("Modules are not equivalent!\n"); log("Verified %s = %s (using brute-force check on %d cases).\n", mod1_name.c_str(), mod2_name.c_str(), checker.counter); return; @@ -448,7 +448,7 @@ struct EvalPass : public Pass { RTLIL::id2cstr(module->name), RTLIL::id2cstr(mod_it.first)); module = mod_it.second; } - if (module == NULL) + if (module == NULL) log_cmd_error("Can't perform EVAL on an empty selection!\n"); ConstEval ce(module); @@ -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"); @@ -594,10 +594,10 @@ struct EvalPass : public Pass { log("\n"); if (undef.size() > 0) { undef.sort_and_unify(); - log("Assumend undef (x) value for the following singals: %s\n\n", log_signal(undef)); + log("Assumed undef (x) value for the following signals: %s\n\n", log_signal(undef)); } } } } EvalPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc index b012bc6a4..9427547f3 100644 --- a/passes/sat/expose.cc +++ b/passes/sat/expose.cc @@ -2,11 +2,11 @@ * 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 @@ -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; } } @@ -237,8 +237,8 @@ struct ExposePass : public Pass { log(" signal path at that wire.\n"); log("\n"); log(" -shared\n"); - log(" only expose those signals that are shared ammong the selected modules.\n"); - log(" this is useful for preparing modules for equivialence checking.\n"); + log(" only expose those signals that are shared among the selected modules.\n"); + log(" this is useful for preparing modules for equivalence checking.\n"); log("\n"); log(" -evert\n"); log(" also turn connections to instances of other modules to additional\n"); @@ -262,7 +262,7 @@ struct ExposePass : public Pass { bool flag_evert_dff = false; std::string sep = "."; - log_header("Executing EXPOSE pass (exposing internal signals as outputs).\n"); + log_header(design, "Executing EXPOSE pass (exposing internal signals as outputs).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -646,5 +646,5 @@ struct ExposePass : public Pass { } } } ExposePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc index fbca35861..77263f6a2 100644 --- a/passes/sat/freduce.cc +++ b/passes/sat/freduce.cc @@ -2,11 +2,11 @@ * 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 @@ -73,7 +73,7 @@ struct FindReducedInputs SigMap &sigmap; drivers_t &drivers; - ezDefaultSAT ez; + ezSatPtr ez; std::set<RTLIL::Cell*> ez_cells; SatGen satgen; @@ -81,7 +81,7 @@ struct FindReducedInputs std::vector<int> sat_pi_uniq_bitvec; FindReducedInputs(SigMap &sigmap, drivers_t &drivers) : - sigmap(sigmap), drivers(drivers), satgen(&ez, &sigmap) + sigmap(sigmap), drivers(drivers), satgen(ez.get(), &sigmap) { satgen.model_undef = true; } @@ -104,30 +104,30 @@ struct FindReducedInputs satgen.setContext(&sigmap, "A"); int sat_a = satgen.importSigSpec(bit).front(); - ez.assume(ez.NOT(satgen.importUndefSigSpec(bit).front())); + ez->assume(ez->NOT(satgen.importUndefSigSpec(bit).front())); satgen.setContext(&sigmap, "B"); int sat_b = satgen.importSigSpec(bit).front(); - ez.assume(ez.NOT(satgen.importUndefSigSpec(bit).front())); + ez->assume(ez->NOT(satgen.importUndefSigSpec(bit).front())); int idx = sat_pi.size(); size_t idx_bits = get_bits(idx); if (sat_pi_uniq_bitvec.size() != idx_bits) { - sat_pi_uniq_bitvec.push_back(ez.frozen_literal(stringf("uniq_%d", int(idx_bits)-1))); + sat_pi_uniq_bitvec.push_back(ez->frozen_literal(stringf("uniq_%d", int(idx_bits)-1))); for (auto &it : sat_pi) - ez.assume(ez.OR(ez.NOT(it.second), ez.NOT(sat_pi_uniq_bitvec.back()))); + ez->assume(ez->OR(ez->NOT(it.second), ez->NOT(sat_pi_uniq_bitvec.back()))); } log_assert(sat_pi_uniq_bitvec.size() == idx_bits); - sat_pi[bit] = ez.frozen_literal(stringf("p, falsei_%s", log_signal(bit))); - ez.assume(ez.IFF(ez.XOR(sat_a, sat_b), sat_pi[bit])); + sat_pi[bit] = ez->frozen_literal(stringf("p, falsei_%s", log_signal(bit))); + ez->assume(ez->IFF(ez->XOR(sat_a, sat_b), sat_pi[bit])); for (size_t i = 0; i < idx_bits; i++) if ((idx & (1 << i)) == 0) - ez.assume(ez.OR(ez.NOT(sat_pi[bit]), ez.NOT(sat_pi_uniq_bitvec[i]))); + ez->assume(ez->OR(ez->NOT(sat_pi[bit]), ez->NOT(sat_pi_uniq_bitvec[i]))); else - ez.assume(ez.OR(ez.NOT(sat_pi[bit]), sat_pi_uniq_bitvec[i])); + ez->assume(ez->OR(ez->NOT(sat_pi[bit]), sat_pi_uniq_bitvec[i])); } void register_cone_worker(std::set<RTLIL::SigBit> &pi, std::set<RTLIL::SigBit> &sigdone, RTLIL::SigBit out) @@ -201,7 +201,7 @@ struct FindReducedInputs model_expr.push_back(sat_pi.at(pi[i])); } - if (!ez.solve(model_expr, model, ez.expression(ezSAT::OpOr, model_expr), ez.XOR(output_a, output_b), ez.NOT(output_undef_a), ez.NOT(output_undef_b))) + if (!ez->solve(model_expr, model, ez->expression(ezSAT::OpOr, model_expr), ez->XOR(output_a, output_b), ez->NOT(output_undef_a), ez->NOT(output_undef_b))) break; int found_count = 0; @@ -229,8 +229,9 @@ struct PerformReduction SigMap &sigmap; drivers_t &drivers; std::set<std::pair<RTLIL::SigBit, RTLIL::SigBit>> &inv_pairs; + pool<SigBit> recursion_guard; - ezDefaultSAT ez; + ezSatPtr ez; SatGen satgen; std::vector<int> sat_pi, sat_out, sat_def; @@ -246,6 +247,15 @@ struct PerformReduction if (sigdepth.count(out) != 0) return sigdepth.at(out); + if (recursion_guard.count(out)) { + string loop_signals; + for (auto loop_bit : recursion_guard) + loop_signals += string(" ") + log_signal(loop_bit); + log_error("Found logic loop:%s\n", loop_signals.c_str()); + } + + recursion_guard.insert(out); + if (drivers.count(out) != 0) { std::pair<RTLIL::Cell*, std::set<RTLIL::SigBit>> &drv = drivers.at(out); if (celldone.count(drv.first) == 0) { @@ -255,20 +265,21 @@ 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); sat_pi.push_back(satgen.importSigSpec(out).front()); - ez.assume(ez.NOT(satgen.importUndefSigSpec(out).front())); + ez->assume(ez->NOT(satgen.importUndefSigSpec(out).front())); sigdepth[out] = 0; } + recursion_guard.erase(out); return sigdepth.at(out); } PerformReduction(SigMap &sigmap, drivers_t &drivers, std::set<std::pair<RTLIL::SigBit, RTLIL::SigBit>> &inv_pairs, std::vector<RTLIL::SigBit> &bits, int cone_size) : - sigmap(sigmap), drivers(drivers), inv_pairs(inv_pairs), satgen(&ez, &sigmap), out_bits(bits), cone_size(cone_size) + sigmap(sigmap), drivers(drivers), inv_pairs(inv_pairs), satgen(ez.get(), &sigmap), out_bits(bits), cone_size(cone_size) { satgen.model_undef = true; @@ -278,15 +289,15 @@ struct PerformReduction for (auto &bit : bits) { out_depth.push_back(register_cone_worker(celldone, sigdepth, bit)); sat_out.push_back(satgen.importSigSpec(bit).front()); - sat_def.push_back(ez.NOT(satgen.importUndefSigSpec(bit).front())); + sat_def.push_back(ez->NOT(satgen.importUndefSigSpec(bit).front())); } if (inv_mode && cone_size > 0) { - if (!ez.solve(sat_out, out_inverted, ez.expression(ezSAT::OpAnd, sat_def))) + if (!ez->solve(sat_out, out_inverted, ez->expression(ezSAT::OpAnd, sat_def))) log_error("Solving for initial model failed!\n"); for (size_t i = 0; i < sat_out.size(); i++) if (out_inverted.at(i)) - sat_out[i] = ez.NOT(sat_out[i]); + sat_out[i] = ez->NOT(sat_out[i]); } else out_inverted = std::vector<bool>(sat_out.size(), false); } @@ -296,8 +307,8 @@ struct PerformReduction if (verbose_level == 1) log(" Finding const value for %s.\n", log_signal(out_bits[idx])); - bool can_be_set = ez.solve(ez.AND(sat_out[idx], sat_def[idx])); - bool can_be_clr = ez.solve(ez.AND(ez.NOT(sat_out[idx]), sat_def[idx])); + bool can_be_set = ez->solve(ez->AND(sat_out[idx], sat_def[idx])); + bool can_be_clr = ez->solve(ez->AND(ez->NOT(sat_out[idx]), sat_def[idx])); log_assert(!can_be_set || !can_be_clr); RTLIL::SigBit value(RTLIL::State::Sx); @@ -355,8 +366,8 @@ struct PerformReduction std::vector<int> sat_set_list, sat_clr_list; for (int idx : bucket) { - sat_set_list.push_back(ez.AND(sat_out[idx], sat_def[idx])); - sat_clr_list.push_back(ez.AND(ez.NOT(sat_out[idx]), sat_def[idx])); + sat_set_list.push_back(ez->AND(sat_out[idx], sat_def[idx])); + sat_clr_list.push_back(ez->AND(ez->NOT(sat_out[idx]), sat_def[idx])); } std::vector<int> modelVars = sat_out; @@ -366,7 +377,7 @@ struct PerformReduction if (verbose_level >= 2) modelVars.insert(modelVars.end(), sat_pi.begin(), sat_pi.end()); - if (ez.solve(modelVars, model, ez.expression(ezSAT::OpOr, sat_set_list), ez.expression(ezSAT::OpOr, sat_clr_list))) + if (ez->solve(modelVars, model, ez->expression(ezSAT::OpOr, sat_set_list), ez->expression(ezSAT::OpOr, sat_clr_list))) { int iter_count = 1; @@ -379,13 +390,13 @@ struct PerformReduction for (int idx : bucket) if (!model[sat_out.size() + idx]) { - sat_set_list.push_back(ez.AND(sat_out[idx], sat_def[idx])); - sat_clr_list.push_back(ez.AND(ez.NOT(sat_out[idx]), sat_def[idx])); + sat_set_list.push_back(ez->AND(sat_out[idx], sat_def[idx])); + sat_clr_list.push_back(ez->AND(ez->NOT(sat_out[idx]), sat_def[idx])); } else { sat_def_list.push_back(sat_def[idx]); } - if (!ez.solve(modelVars, model, ez.expression(ezSAT::OpOr, sat_set_list), ez.expression(ezSAT::OpOr, sat_clr_list), ez.expression(ezSAT::OpAnd, sat_def_list))) + if (!ez->solve(modelVars, model, ez->expression(ezSAT::OpOr, sat_set_list), ez->expression(ezSAT::OpOr, sat_clr_list), ez->expression(ezSAT::OpAnd, sat_def_list))) break; iter_count++; } @@ -431,7 +442,7 @@ struct PerformReduction for (int idx2 : bucket) if (idx != idx2) sat_def_list.push_back(sat_def[idx2]); - if (ez.solve(ez.NOT(sat_def[idx]), ez.expression(ezSAT::OpOr, sat_def_list))) + if (ez->solve(ez->NOT(sat_def[idx]), ez->expression(ezSAT::OpOr, sat_def_list))) undef_slaves.push_back(idx); } @@ -446,7 +457,7 @@ struct PerformReduction out_depth[idx] = std::numeric_limits<int>::max(); if (verbose_level >= 1) { - log("%s Found %d equivialent signals:", indt, int(bucket.size())); + log("%s Found %d equivalent signals:", indt, int(bucket.size())); for (int idx : bucket) log("%s%s%s", idx == bucket.front() ? " " : ", ", out_inverted[idx] ? "~" : "", log_signal(out_bits[idx])); log("\n"); @@ -495,7 +506,7 @@ struct PerformReduction std::vector<RTLIL::SigBit> r_sigbits; for (int idx : r) r_sigbits.push_back(out_bits[idx]); - log(" Found group of %d equivialent signals: %s\n", int(r.size()), log_signal(r_sigbits)); + log(" Found group of %d equivalent signals: %s\n", int(r.size()), log_signal(r_sigbits)); } std::vector<int> undef_slaves; @@ -505,7 +516,7 @@ struct PerformReduction for (int idx2 : r) if (idx != idx2) sat_def_list.push_back(sat_def[idx2]); - if (ez.solve(ez.NOT(sat_def[idx]), ez.expression(ezSAT::OpOr, sat_def_list))) + if (ez->solve(ez->NOT(sat_def[idx]), ez->expression(ezSAT::OpOr, sat_def_list))) undef_slaves.push_back(idx); } @@ -681,7 +692,7 @@ struct FreduceWorker if (!dump_prefix.empty()) dump(); - log(" Rewiring %d equivialent groups:\n", int(equiv.size())); + log(" Rewiring %d equivalent groups:\n", int(equiv.size())); int rewired_sigbits = 0; for (auto &grp : equiv) { @@ -755,7 +766,7 @@ struct FreducePass : public Pass { log(" freduce [options] [selection]\n"); log("\n"); log("This pass performs functional reduction in the circuit. I.e. if two nodes are\n"); - log("equivialent, they are merged to one node and one of the redundant drivers is\n"); + log("equivalent, they are merged to one node and one of the redundant drivers is\n"); log("disconnected. A subsequent call to 'clean' will remove the redundant drivers.\n"); log("\n"); log(" -v, -vv\n"); @@ -773,7 +784,7 @@ struct FreducePass : public Pass { log(" operation. this is mostly used for debugging the freduce command.\n"); log("\n"); log("This pass is undef-aware, i.e. it considers don't-care values for detecting\n"); - log("equivialent nodes.\n"); + log("equivalent nodes.\n"); log("\n"); log("All selected wires are considered for rewiring. The selected cells cover the\n"); log("circuit that is analyzed.\n"); @@ -787,7 +798,7 @@ struct FreducePass : public Pass { inv_mode = false; dump_prefix = std::string(); - log_header("Executing FREDUCE pass (perform functional reduction).\n"); + log_header(design, "Executing FREDUCE pass (perform functional reduction).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -825,5 +836,5 @@ struct FreducePass : public Pass { log("Rewired a total of %d signal bits.\n", bitcount); } } FreducePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc index 9853cd0c6..4854e19bf 100644 --- a/passes/sat/miter.cc +++ b/passes/sat/miter.cc @@ -2,11 +2,11 @@ * 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 @@ -32,7 +32,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL: bool flag_make_assert = false; bool flag_flatten = false; - log_header("Executing MITER pass (creating miter circuit).\n"); + log_header(design, "Executing MITER pass (creating miter circuit).\n"); size_t argidx; for (argidx = 2; argidx < args.size(); argidx++) @@ -61,7 +61,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL: } if (argidx+3 != args.size() || args[argidx].substr(0, 1) == "-") that->cmd_error(args, argidx, "command argument error"); - + RTLIL::IdString gold_name = RTLIL::escape_id(args[argidx++]); RTLIL::IdString gate_name = RTLIL::escape_id(args[argidx++]); RTLIL::IdString miter_name = RTLIL::escape_id(args[argidx++]); @@ -71,7 +71,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL: if (design->modules_.count(gate_name) == 0) log_cmd_error("Can't find gate module %s!\n", gate_name.c_str()); if (design->modules_.count(miter_name) != 0) - log_cmd_error("There is already a module %s!\n", gate_name.c_str()); + log_cmd_error("There is already a module %s!\n", miter_name.c_str()); RTLIL::Module *gold_module = design->modules_.at(gold_name); RTLIL::Module *gate_module = design->modules_.at(gate_name); @@ -254,7 +254,80 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL: if (flag_flatten) { log_push(); - Pass::call_on_module(design, miter_module, "flatten; opt_const -keepdc -undriven;;"); + Pass::call_on_module(design, miter_module, "flatten; opt_expr -keepdc -undriven;;"); + log_pop(); + } +} + +void create_miter_assert(struct Pass *that, std::vector<std::string> args, RTLIL::Design *design) +{ + bool flag_make_outputs = false; + bool flag_flatten = false; + + log_header(design, "Executing MITER pass (creating miter circuit).\n"); + + size_t argidx; + for (argidx = 2; argidx < args.size(); argidx++) + { + if (args[argidx] == "-make_outputs") { + flag_make_outputs = true; + continue; + } + if (args[argidx] == "-flatten") { + flag_flatten = true; + continue; + } + break; + } + if ((argidx+1 != args.size() && argidx+2 != args.size()) || args[argidx].substr(0, 1) == "-") + that->cmd_error(args, argidx, "command argument error"); + + IdString module_name = RTLIL::escape_id(args[argidx++]); + IdString miter_name = argidx < args.size() ? RTLIL::escape_id(args[argidx++]) : ""; + + if (design->modules_.count(module_name) == 0) + log_cmd_error("Can't find module %s!\n", module_name.c_str()); + if (!miter_name.empty() && design->modules_.count(miter_name) != 0) + log_cmd_error("There is already a module %s!\n", miter_name.c_str()); + + Module *module = design->module(module_name); + + if (!miter_name.empty()) { + module = module->clone(); + module->name = miter_name; + design->add(module); + } + + if (!flag_make_outputs) + for (auto wire : module->wires()) + wire->port_output = false; + + Wire *trigger = module->addWire("\\trigger"); + trigger->port_output = true; + module->fixup_ports(); + + if (flag_flatten) { + log_push(); + Pass::call_on_module(design, module, "flatten;;"); + log_pop(); + } + + SigSpec or_signals; + vector<Cell*> cell_list = module->cells(); + for (auto cell : cell_list) { + if (cell->type == "$assert") { + SigBit is_active = module->Nex(NEW_ID, cell->getPort("\\A"), State::S1); + SigBit is_enabled = module->Eqx(NEW_ID, cell->getPort("\\EN"), State::S1); + or_signals.append(module->And(NEW_ID, is_active, is_enabled)); + module->remove(cell); + } + } + + module->addReduceOr(NEW_ID, or_signals, trigger); + + if (flag_flatten) { + log_push(); + Pass::call_on_module(design, module, "opt_expr -keepdc -undriven;;"); log_pop(); } } @@ -267,7 +340,7 @@ struct MiterPass : public Pass { log("\n"); log(" miter -equiv [options] gold_name gate_name miter_name\n"); log("\n"); - log("Creates a miter circuit for equivialence checking. The gold- and gate- modules\n"); + log("Creates a miter circuit for equivalence checking. The gold- and gate- modules\n"); log("must have the same interfaces. The miter circuit will have all inputs of the\n"); log("two source modules, prefixed with 'in_'. The miter circuit has a 'trigger'\n"); log("output that goes high if an output mismatch between the two source modules is\n"); @@ -288,7 +361,21 @@ struct MiterPass : public Pass { log(" also create an 'assert' cell that checks if trigger is always low.\n"); log("\n"); log(" -flatten\n"); - log(" call 'flatten; opt_const -keepdc -undriven;;' on the miter circuit.\n"); + log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n"); + log("\n"); + log("\n"); + log(" miter -assert [options] module [miter_name]\n"); + log("\n"); + log("Creates a miter circuit for property checking. All input ports are kept,\n"); + log("output ports are discarded. An additional output 'trigger' is created that\n"); + log("goes high when an assert is violated. Without a miter_name, the existing\n"); + log("module is modified.\n"); + log("\n"); + log(" -make_outputs\n"); + log(" keep module output ports.\n"); + log("\n"); + log(" -flatten\n"); + log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) @@ -298,8 +385,13 @@ struct MiterPass : public Pass { return; } + if (args.size() > 1 && args[1] == "-assert") { + create_miter_assert(this, args, design); + return; + } + log_cmd_error("Missing mode parameter!\n"); } } MiterPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index 1aae421f0..c3cb435d1 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -2,11 +2,11 @@ * 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 @@ -41,16 +41,17 @@ struct SatHelper RTLIL::Design *design; RTLIL::Module *module; - ezDefaultSAT ez; SigMap sigmap; CellTypes ct; + + ezSatPtr ez; SatGen satgen; // additional constraints std::vector<std::pair<std::string, std::string>> sets, prove, prove_x, sets_init; std::map<int, std::vector<std::pair<std::string, std::string>>> sets_at; std::map<int, std::vector<std::string>> unsets_at; - bool prove_asserts; + bool prove_asserts, set_assumes; // undef constraints bool enable_undef, set_init_def, set_init_undef, set_init_zero, ignore_unknown_cells; @@ -65,7 +66,7 @@ struct SatHelper bool gotTimeout; SatHelper(RTLIL::Design *design, RTLIL::Module *module, bool enable_undef) : - design(design), module(module), sigmap(module), ct(design), satgen(&ez, &sigmap) + design(design), module(module), sigmap(module), ct(design), satgen(ez.get(), &sigmap) { this->enable_undef = enable_undef; satgen.model_undef = enable_undef; @@ -155,7 +156,7 @@ struct SatHelper if (set_init_def) { RTLIL::SigSpec rem = satgen.initial_state.export_all(); std::vector<int> undef_rem = satgen.importUndefSigSpec(rem, 1); - ez.assume(ez.NOT(ez.expression(ezSAT::OpOr, undef_rem))); + ez->assume(ez->NOT(ez->expression(ezSAT::OpOr, undef_rem))); } if (set_init_undef) { @@ -179,7 +180,7 @@ struct SatHelper log("Final constraint equation: %s = %s\n\n", log_signal(big_lhs), log_signal(big_rhs)); check_undef_enabled(big_lhs), check_undef_enabled(big_rhs); - ez.assume(satgen.signals_eq(big_lhs, big_rhs, 1)); + ez->assume(satgen.signals_eq(big_lhs, big_rhs, 1)); } void setup(int timestep = -1) @@ -250,7 +251,7 @@ struct SatHelper log("Final constraint equation: %s = %s\n", log_signal(big_lhs), log_signal(big_rhs)); check_undef_enabled(big_lhs), check_undef_enabled(big_rhs); - ez.assume(satgen.signals_eq(big_lhs, big_rhs, timestep)); + ez->assume(satgen.signals_eq(big_lhs, big_rhs, timestep)); // 0 = sets_def // 1 = sets_any_undef @@ -310,28 +311,36 @@ struct SatHelper log("Import %s constraint for this timestep: %s\n", t == 0 ? "def" : t == 1 ? "any_undef" : "all_undef", log_signal(sig)); std::vector<int> undef_sig = satgen.importUndefSigSpec(sig, timestep); if (t == 0) - ez.assume(ez.NOT(ez.expression(ezSAT::OpOr, undef_sig))); + ez->assume(ez->NOT(ez->expression(ezSAT::OpOr, undef_sig))); if (t == 1) - ez.assume(ez.expression(ezSAT::OpOr, undef_sig)); + ez->assume(ez->expression(ezSAT::OpOr, undef_sig)); if (t == 2) - ez.assume(ez.expression(ezSAT::OpAnd, undef_sig)); + ez->assume(ez->expression(ezSAT::OpAnd, undef_sig)); } int import_cell_counter = 0; - for (auto &c : module->cells_) - if (design->selected(module, c.second)) { - // log("Import cell: %s\n", RTLIL::id2cstr(c.first)); - if (satgen.importCell(c.second, timestep)) { - for (auto &p : c.second->connections()) - if (ct.cell_output(c.second->type, p.first)) - show_drivers.insert(sigmap(p.second), c.second); + for (auto cell : module->cells()) + if (design->selected(module, cell)) { + // log("Import cell: %s\n", RTLIL::id2cstr(cell->name)); + if (satgen.importCell(cell, timestep)) { + for (auto &p : cell->connections()) + if (ct.cell_output(cell->type, p.first)) + show_drivers.insert(sigmap(p.second), cell); import_cell_counter++; } else if (ignore_unknown_cells) - log_warning("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(c.first), RTLIL::id2cstr(c.second->type)); + log_warning("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); else - log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(c.first), RTLIL::id2cstr(c.second->type)); + log_error("Failed to import cell %s (type %s) to SAT database.\n", RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); } log("Imported %d cells to SAT database.\n", import_cell_counter); + + if (set_assumes) { + RTLIL::SigSpec assumes_a, assumes_en; + satgen.getAssumes(assumes_a, assumes_en, timestep); + for (int i = 0; i < GetSize(assumes_a); i++) + log("Import constraint from assume cell: %s when %s.\n", log_signal(assumes_a[i]), log_signal(assumes_en[i])); + ez->assume(satgen.importAssumes(timestep)); + } } int setup_proof(int timestep = -1) @@ -401,7 +410,7 @@ struct SatHelper std::vector<int> undef_rhs = satgen.importUndefSigSpec(big_rhs, timestep); for (size_t i = 0; i < value_lhs.size(); i++) - prove_bits.push_back(ez.OR(undef_lhs.at(i), ez.AND(ez.NOT(undef_rhs.at(i)), ez.NOT(ez.XOR(value_lhs.at(i), value_rhs.at(i)))))); + prove_bits.push_back(ez->OR(undef_lhs.at(i), ez->AND(ez->NOT(undef_rhs.at(i)), ez->NOT(ez->XOR(value_lhs.at(i), value_rhs.at(i)))))); } if (prove_asserts) { @@ -412,22 +421,22 @@ struct SatHelper prove_bits.push_back(satgen.importAsserts(timestep)); } - return ez.expression(ezSAT::OpAnd, prove_bits); + return ez->expression(ezSAT::OpAnd, prove_bits); } void force_unique_state(int timestep_from, int timestep_to) { RTLIL::SigSpec state_signals = satgen.initial_state.export_all(); for (int i = timestep_from; i < timestep_to; i++) - ez.assume(ez.NOT(satgen.signals_eq(state_signals, state_signals, i, timestep_to))); + ez->assume(ez->NOT(satgen.signals_eq(state_signals, state_signals, i, timestep_to))); } bool solve(const std::vector<int> &assumptions) { log_assert(gotTimeout == false); - ez.setSolverTimeout(timeout); - bool success = ez.solve(modelExpressions, modelValues, assumptions); - if (ez.getSolverTimoutStatus()) + ez->setSolverTimeout(timeout); + bool success = ez->solve(modelExpressions, modelValues, assumptions); + if (ez->getSolverTimoutStatus()) gotTimeout = true; return success; } @@ -435,9 +444,9 @@ struct SatHelper bool solve(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0, int f = 0) { log_assert(gotTimeout == false); - ez.setSolverTimeout(timeout); - bool success = ez.solve(modelExpressions, modelValues, a, b, c, d, e, f); - if (ez.getSolverTimoutStatus()) + ez->setSolverTimeout(timeout); + bool success = ez->solve(modelExpressions, modelValues, a, b, c, d, e, f); + if (ez->getSolverTimoutStatus()) gotTimeout = true; return success; } @@ -478,7 +487,7 @@ struct SatHelper maybe_undef.push_back(modelExpressions.at(modelExpressions.size()/2 + i)); backupValues.swap(modelValues); - if (!solve(ez.expression(ezSAT::OpAnd, must_undef), ez.expression(ezSAT::OpOr, maybe_undef))) + if (!solve(ez->expression(ezSAT::OpAnd, must_undef), ez->expression(ezSAT::OpOr, maybe_undef))) break; } @@ -597,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"); @@ -621,11 +630,11 @@ struct SatHelper "---------------------------------------------------------------------------------------------------"; if (last_timestep == -2) { log(max_timestep > 0 ? " Time " : " "); - log("%-*s %10s %10s %*s\n", maxModelName+10, "Signal Name", "Dec", "Hex", maxModelWidth+5, "Bin"); + log("%-*s %11s %9s %*s\n", maxModelName+5, "Signal Name", "Dec", "Hex", maxModelWidth+3, "Bin"); } log(max_timestep > 0 ? " ---- " : " "); - log("%*.*s %10.10s %10.10s %*.*s\n", maxModelName+10, maxModelName+10, - hline, hline, hline, maxModelWidth+5, maxModelWidth+5, hline); + log("%*.*s %11.11s %9.9s %*.*s\n", maxModelName+5, maxModelName+5, + hline, hline, hline, maxModelWidth+3, maxModelWidth+3, hline); last_timestep = info.timestep; } @@ -638,9 +647,9 @@ struct SatHelper log(" "); if (info.width <= 32 && !found_undef) - log("%-*s %10d %10x %*s\n", maxModelName+10, info.description.c_str(), value.as_int(), value.as_int(), maxModelWidth+5, value.as_string().c_str()); + log("%-*s %11d %9x %*s\n", maxModelName+5, info.description.c_str(), value.as_int(), value.as_int(), maxModelWidth+3, value.as_string().c_str()); else - log("%-*s %10s %10s %*s\n", maxModelName+10, info.description.c_str(), "--", "--", maxModelWidth+5, value.as_string().c_str()); + log("%-*s %11s %9s %*s\n", maxModelName+5, info.description.c_str(), "--", "--", maxModelWidth+3, value.as_string().c_str()); } if (last_timestep == -2) @@ -671,7 +680,7 @@ struct SatHelper fprintf(f, " %s\n", stime); fprintf(f, "$end\n"); fprintf(f, "$version\n"); - fprintf(f, " Generated by %s\n", yosys_version_str); + fprintf(f, " Generated by %s\n", yosys_version_str); fprintf(f, "$end\n"); fprintf(f, "$comment\n"); fprintf(f, " Generated from SAT problem in module %s (declared at %s)\n", @@ -750,6 +759,80 @@ struct SatHelper fclose(f); } + void dump_model_to_json(std::string json_file_name) + { + FILE *f = fopen(json_file_name.c_str(), "w"); + if (!f) + log_cmd_error("Can't open output file `%s' for writing: %s\n", json_file_name.c_str(), strerror(errno)); + + log("Dumping SAT model to WaveJSON file '%s'.\n", json_file_name.c_str()); + + int mintime = 1, maxtime = 0, maxwidth = 0;; + dict<string, pair<int, dict<int, Const>>> wavedata; + + for (auto &info : modelInfo) + { + Const value; + for (int i = 0; i < info.width; i++) { + value.bits.push_back(modelValues.at(info.offset+i) ? RTLIL::State::S1 : RTLIL::State::S0); + if (enable_undef && modelValues.at(modelExpressions.size()/2 + info.offset + i)) + value.bits.back() = RTLIL::State::Sx; + } + + wavedata[info.description].first = info.width; + wavedata[info.description].second[info.timestep] = value; + mintime = min(mintime, info.timestep); + maxtime = max(maxtime, info.timestep); + maxwidth = max(maxwidth, info.width); + } + + fprintf(f, "{ \"signal\": ["); + bool fist_wavedata = true; + for (auto &wd : wavedata) + { + fprintf(f, "%s", fist_wavedata ? "\n" : ",\n"); + fist_wavedata = false; + + vector<string> data; + string name = wd.first.c_str(); + while (name.substr(0, 1) == "\\") + name = name.substr(1); + + fprintf(f, " { \"name\": \"%s\", \"wave\": \"", name.c_str()); + for (int i = mintime; i <= maxtime; i++) { + if (wd.second.second.count(i)) { + string this_data = wd.second.second[i].as_string(); + char ch = '='; + if (wd.second.first == 1) + ch = this_data[0]; + if (!data.empty() && data.back() == this_data) { + fprintf(f, "."); + } else { + data.push_back(this_data); + fprintf(f, "%c", ch); + } + } else { + data.push_back(""); + fprintf(f, "4"); + } + } + if (wd.second.first != 1) { + fprintf(f, "\", \"data\": ["); + for (int i = 0; i < GetSize(data); i++) + fprintf(f, "%s\"%s\"", i ? ", " : "", data[i].c_str()); + fprintf(f, "] }"); + } else { + fprintf(f, "\" }"); + } + } + fprintf(f, "\n ],\n"); + fprintf(f, " \"config\": {\n"); + fprintf(f, " \"hscale\": %.2f\n", maxwidth / 4.0); + fprintf(f, " }\n"); + fprintf(f, "}\n"); + fclose(f); + } + void invalidate_model(bool max_undef) { std::vector<int> clause; @@ -758,12 +841,12 @@ struct SatHelper int bit = modelExpressions.at(i), bit_undef = modelExpressions.at(modelExpressions.size()/2 + i); bool val = modelValues.at(i), val_undef = modelValues.at(modelExpressions.size()/2 + i); if (!max_undef || !val_undef) - clause.push_back(val_undef ? ez.NOT(bit_undef) : val ? ez.NOT(bit) : bit); + clause.push_back(val_undef ? ez->NOT(bit_undef) : val ? ez->NOT(bit) : bit); } } else for (size_t i = 0; i < modelExpressions.size(); i++) - clause.push_back(modelValues.at(i) ? ez.NOT(modelExpressions.at(i)) : modelExpressions.at(i)); - ez.assume(ez.expression(ezSAT::OpOr, clause)); + clause.push_back(modelValues.at(i) ? ez->NOT(modelExpressions.at(i)) : modelExpressions.at(i)); + ez->assume(ez->expression(ezSAT::OpOr, clause)); } }; @@ -853,6 +936,9 @@ struct SatPass : public Pass { log(" -show-inputs, -show-outputs, -show-ports\n"); log(" add all module (input/output) ports to the list of shown signals\n"); log("\n"); + log(" -show-regs, -show-public, -show-all\n"); + log(" show all registers, show signals with 'public' names, show all signals\n"); + log("\n"); log(" -ignore_div_by_zero\n"); log(" ignore all solutions that involve a division by zero\n"); log("\n"); @@ -865,11 +951,17 @@ struct SatPass : public Pass { log(" set up a sequential problem with <N> time steps. The steps will\n"); log(" be numbered from 1 to N.\n"); log("\n"); + log(" note: for large <N> it can be significantly faster to use\n"); + log(" -tempinduct-baseonly -maxsteps <N> instead of -seq <N>.\n"); + log("\n"); log(" -set-at <N> <signal> <value>\n"); log(" -unset-at <N> <signal>\n"); log(" set or unset the specified signal to the specified value in the\n"); log(" given timestep. this has priority over a -set for the same signal.\n"); log("\n"); + log(" -set-assumes\n"); + log(" set all assumptions provided via $assume cells\n"); + log("\n"); log(" -set-def-at <N> <signal>\n"); log(" -set-any-undef-at <N> <signal>\n"); log(" -set-all-undef-at <N> <signal>\n"); @@ -890,6 +982,9 @@ struct SatPass : public Pass { log(" -dump_vcd <vcd-file-name>\n"); log(" dump SAT model (counter example in proof) to VCD file\n"); log("\n"); + log(" -dump_json <json-file-name>\n"); + log(" dump SAT model (counter example in proof) to a WaveJSON file.\n"); + log("\n"); log(" -dump_cnf <cnf-file-name>\n"); log(" dump CNF of SAT problem (in DIMACS format). in temporal induction\n"); log(" proofs this is the CNF of the first induction step.\n"); @@ -898,7 +993,7 @@ struct SatPass : public Pass { log("is passed, a temporal induction proof is performed.\n"); log("\n"); log(" -tempinduct\n"); - log(" Perform a temporal induction proof. In a temporalinduction proof it is\n"); + log(" Perform a temporal induction proof. In a temporal induction proof it is\n"); log(" proven that the condition holds forever after the number of time steps\n"); log(" specified using -seq.\n"); log("\n"); @@ -906,12 +1001,26 @@ struct SatPass : public Pass { log(" Perform a temporal induction proof. Assume an initial state with all\n"); log(" registers set to defined values for the induction step.\n"); log("\n"); + log(" -tempinduct-baseonly\n"); + log(" Run only the basecase half of temporal induction (requires -maxsteps)\n"); + log("\n"); + log(" -tempinduct-inductonly\n"); + log(" Run only the induction half of temporal induction\n"); + log("\n"); + log(" -tempinduct-skip <N>\n"); + log(" Skip the first <N> steps of the induction proof.\n"); + log("\n"); + log(" note: this will assume that the base case holds for <N> steps.\n"); + log(" this must be proven independently with \"-tempinduct-baseonly\n"); + log(" -maxsteps <N>\". Use -initsteps if you just want to set a\n"); + log(" minimal induction length.\n"); + log("\n"); log(" -prove <signal> <value>\n"); log(" Attempt to proof that <signal> is always <value>.\n"); log("\n"); log(" -prove-x <signal> <value>\n"); log(" Like -prove, but an undef (x) bit in the lhs matches any value on\n"); - log(" the right hand side. Useful for equivialence checking.\n"); + log(" the right hand side. Useful for equivalence checking.\n"); log("\n"); log(" -prove-asserts\n"); log(" Prove that all asserts in the design hold.\n"); @@ -924,6 +1033,13 @@ struct SatPass : public Pass { log("\n"); log(" -initsteps <N>\n"); log(" Set initial length for the induction.\n"); + log(" This will speed up the search of the right induction length\n"); + log(" for deep induction proofs.\n"); + log("\n"); + log(" -stepsize <N>\n"); + log(" Increase the size of the induction proof in steps of <N>.\n"); + log(" This will speed up the search of the right induction length\n"); + log(" for deep induction proofs.\n"); log("\n"); log(" -timeout <N>\n"); log(" Maximum number of seconds a single SAT instance may take.\n"); @@ -951,10 +1067,13 @@ struct SatPass : public Pass { bool verify = false, fail_on_timeout = false, enable_undef = false, set_def_inputs = false; bool ignore_div_by_zero = false, set_init_undef = false, set_init_zero = false, max_undef = false; bool tempinduct = false, prove_asserts = false, show_inputs = false, show_outputs = false; + bool show_regs = false, show_public = false, show_all = false; bool ignore_unknown_cells = false, falsify = false, tempinduct_def = false, set_init_def = false; - std::string vcd_file_name, cnf_file_name; + bool tempinduct_baseonly = false, tempinduct_inductonly = false, set_assumes = false; + int tempinduct_skip = 0, stepsize = 1; + std::string vcd_file_name, json_file_name, cnf_file_name; - log_header("Executing SAT pass (solving SAT problems in the circuit).\n"); + log_header(design, "Executing SAT pass (solving SAT problems in the circuit).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -996,6 +1115,10 @@ struct SatPass : public Pass { initsteps = atoi(args[++argidx].c_str()); continue; } + if (args[argidx] == "-stepsize" && argidx+1 < args.size()) { + stepsize = max(1, atoi(args[++argidx].c_str())); + continue; + } if (args[argidx] == "-ignore_div_by_zero") { ignore_div_by_zero = true; continue; @@ -1035,6 +1158,10 @@ struct SatPass : public Pass { enable_undef = true; continue; } + if (args[argidx] == "-set-assumes") { + set_assumes = true; + continue; + } if (args[argidx] == "-tempinduct") { tempinduct = true; continue; @@ -1044,6 +1171,20 @@ struct SatPass : public Pass { tempinduct_def = true; continue; } + if (args[argidx] == "-tempinduct-baseonly") { + tempinduct = true; + tempinduct_baseonly = true; + continue; + } + if (args[argidx] == "-tempinduct-inductonly") { + tempinduct = true; + tempinduct_inductonly = true; + continue; + } + if (args[argidx] == "-tempinduct-skip" && argidx+1 < args.size()) { + tempinduct_skip = atoi(args[++argidx].c_str()); + continue; + } if (args[argidx] == "-prove" && argidx+2 < args.size()) { std::string lhs = args[++argidx]; std::string rhs = args[++argidx]; @@ -1135,6 +1276,18 @@ struct SatPass : public Pass { show_outputs = true; continue; } + if (args[argidx] == "-show-regs") { + show_regs = true; + continue; + } + if (args[argidx] == "-show-public") { + show_public = true; + continue; + } + if (args[argidx] == "-show-all") { + show_all = true; + continue; + } if (args[argidx] == "-ignore_unknown_cells") { ignore_unknown_cells = true; continue; @@ -1143,6 +1296,10 @@ struct SatPass : public Pass { vcd_file_name = args[++argidx]; continue; } + if (args[argidx] == "-dump_json" && argidx+1 < args.size()) { + json_file_name = args[++argidx]; + continue; + } if (args[argidx] == "-dump_cnf" && argidx+1 < args.size()) { cnf_file_name = args[++argidx]; continue; @@ -1152,14 +1309,12 @@ struct SatPass : public Pass { extra_args(args, argidx, design); RTLIL::Module *module = NULL; - for (auto &mod_it : design->modules_) - if (design->selected(mod_it.second)) { - if (module) - log_cmd_error("Only one module must be selected for the SAT pass! (selected: %s and %s)\n", - RTLIL::id2cstr(module->name), RTLIL::id2cstr(mod_it.first)); - module = mod_it.second; - } - if (module == NULL) + for (auto mod : design->selected_modules()) { + if (module) + log_cmd_error("Only one module must be selected for the SAT pass! (selected: %s and %s)\n", log_id(module), log_id(mod)); + module = mod; + } + if (module == NULL) log_cmd_error("Can't perform SAT on an empty selection!\n"); if (!prove.size() && !prove_x.size() && !prove_asserts && tempinduct) @@ -1192,6 +1347,29 @@ struct SatPass : public Pass { shows.push_back(it.second->name.str()); } + if (show_regs) { + pool<Wire*> reg_wires; + for (auto cell : module->cells()) { + if (cell->type == "$dff" || cell->type.substr(0, 6) == "$_DFF_") + for (auto bit : cell->getPort("\\Q")) + if (bit.wire) + reg_wires.insert(bit.wire); + } + for (auto wire : reg_wires) + shows.push_back(wire->name.str()); + } + + if (show_public) { + for (auto wire : module->wires()) + if (wire->name[0] == '\\') + shows.push_back(wire->name.str()); + } + + if (show_all) { + for (auto wire : module->wires()) + shows.push_back(wire->name.str()); + } + if (tempinduct) { if (loopcount > 0 || max_undef) @@ -1199,8 +1377,10 @@ struct SatPass : public Pass { SatHelper basecase(design, module, enable_undef); SatHelper inductstep(design, module, enable_undef); + bool basecase_setup_init = true; basecase.sets = sets; + basecase.set_assumes = set_assumes; basecase.prove = prove; basecase.prove_x = prove_x; basecase.prove_asserts = prove_asserts; @@ -1222,10 +1402,11 @@ struct SatPass : public Pass { basecase.ignore_unknown_cells = ignore_unknown_cells; for (int timestep = 1; timestep <= seq_len; timestep++) - basecase.setup(timestep); - basecase.setup_init(); + if (!tempinduct_inductonly) + basecase.setup(timestep); inductstep.sets = sets; + inductstep.set_assumes = set_assumes; inductstep.prove = prove; inductstep.prove_x = prove_x; inductstep.prove_asserts = prove_asserts; @@ -1237,12 +1418,14 @@ struct SatPass : public Pass { inductstep.satgen.ignore_div_by_zero = ignore_div_by_zero; inductstep.ignore_unknown_cells = ignore_unknown_cells; - inductstep.setup(1); - inductstep.ez.assume(inductstep.setup_proof(1)); + if (!tempinduct_baseonly) { + inductstep.setup(1); + inductstep.ez->assume(inductstep.setup_proof(1)); + } if (tempinduct_def) { std::vector<int> undef_state = inductstep.satgen.importUndefSigSpec(inductstep.satgen.initial_state.export_all(), 1); - inductstep.ez.assume(inductstep.ez.NOT(inductstep.ez.expression(ezSAT::OpOr, undef_state))); + inductstep.ez->assume(inductstep.ez->NOT(inductstep.ez->expression(ezSAT::OpOr, undef_state))); } for (int inductlen = 1; inductlen <= maxsteps || maxsteps == 0; inductlen++) @@ -1251,81 +1434,120 @@ struct SatPass : public Pass { // phase 1: proving base case - basecase.setup(seq_len + inductlen); - int property = basecase.setup_proof(seq_len + inductlen); - basecase.generate_model(); - - if (inductlen > 1) - basecase.force_unique_state(seq_len + 1, seq_len + inductlen); + if (!tempinduct_inductonly) + { + basecase.setup(seq_len + inductlen); + int property = basecase.setup_proof(seq_len + inductlen); + basecase.generate_model(); - log("\n[base case] Solving problem with %d variables and %d clauses..\n", - basecase.ez.numCnfVariables(), basecase.ez.numCnfClauses()); + if (basecase_setup_init) { + basecase.setup_init(); + basecase_setup_init = false; + } - if (basecase.solve(basecase.ez.NOT(property))) { - log("SAT temporal induction proof finished - model found for base case: FAIL!\n"); - print_proof_failed(); - basecase.print_model(); - if(!vcd_file_name.empty()) - basecase.dump_model_to_vcd(vcd_file_name); - goto tip_failed; - } + if (inductlen > 1) + basecase.force_unique_state(seq_len + 1, seq_len + inductlen); - if (basecase.gotTimeout) - goto timeout; + if (tempinduct_skip < inductlen) + { + log("\n[base case %d] Solving problem with %d variables and %d clauses..\n", + inductlen, basecase.ez->numCnfVariables(), basecase.ez->numCnfClauses()); + + if (basecase.solve(basecase.ez->NOT(property))) { + log("SAT temporal induction proof finished - model found for base case: FAIL!\n"); + print_proof_failed(); + basecase.print_model(); + if(!vcd_file_name.empty()) + basecase.dump_model_to_vcd(vcd_file_name); + if(!json_file_name.empty()) + basecase.dump_model_to_json(json_file_name); + goto tip_failed; + } + + if (basecase.gotTimeout) + goto timeout; - log("Base case for induction length %d proven.\n", inductlen); - basecase.ez.assume(property); + log("Base case for induction length %d proven.\n", inductlen); + } + else + { + log("\n[base case %d] Skipping prove for this step (-tempinduct-skip %d).", + inductlen, tempinduct_skip); + log("\n[base case %d] Problem size so far: %d variables and %d clauses.\n", + inductlen, basecase.ez->numCnfVariables(), basecase.ez->numCnfClauses()); + } + basecase.ez->assume(property); + } // phase 2: proving induction step - inductstep.setup(inductlen + 1); - property = inductstep.setup_proof(inductlen + 1); - inductstep.generate_model(); - - if (inductlen > 1) - inductstep.force_unique_state(1, inductlen + 1); - - if (inductlen < initsteps) - { - log("\n[induction step] Skipping problem with %d variables and %d clauses (below initsteps).\n", - inductstep.ez.numCnfVariables(), inductstep.ez.numCnfClauses()); - inductstep.ez.assume(property); - } - else + if (!tempinduct_baseonly) { - if (!cnf_file_name.empty()) - { - FILE *f = fopen(cnf_file_name.c_str(), "w"); - if (!f) - log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno)); + inductstep.setup(inductlen + 1); + int property = inductstep.setup_proof(inductlen + 1); + inductstep.generate_model(); - log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str()); - cnf_file_name.clear(); + if (inductlen > 1) + inductstep.force_unique_state(1, inductlen + 1); - inductstep.ez.printDIMACS(f, false); - fclose(f); + if (inductlen <= tempinduct_skip || inductlen <= initsteps || inductlen % stepsize != 0) + { + if (inductlen < tempinduct_skip) + log("\n[induction step %d] Skipping prove for this step (-tempinduct-skip %d).", + inductlen, tempinduct_skip); + if (inductlen < initsteps) + log("\n[induction step %d] Skipping prove for this step (-initsteps %d).", + inductlen, tempinduct_skip); + if (inductlen % stepsize != 0) + log("\n[induction step %d] Skipping prove for this step (-stepsize %d).", + inductlen, stepsize); + log("\n[induction step %d] Problem size so far: %d variables and %d clauses.\n", + inductlen, inductstep.ez->numCnfVariables(), inductstep.ez->numCnfClauses()); + inductstep.ez->assume(property); } - - log("\n[induction step] Solving problem with %d variables and %d clauses..\n", - inductstep.ez.numCnfVariables(), inductstep.ez.numCnfClauses()); - - if (!inductstep.solve(inductstep.ez.NOT(property))) { - if (inductstep.gotTimeout) - goto timeout; - log("Induction step proven: SUCCESS!\n"); - print_qed(); - goto tip_success; + else + { + if (!cnf_file_name.empty()) + { + FILE *f = fopen(cnf_file_name.c_str(), "w"); + if (!f) + log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno)); + + log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str()); + cnf_file_name.clear(); + + inductstep.ez->printDIMACS(f, false); + fclose(f); + } + + log("\n[induction step %d] Solving problem with %d variables and %d clauses..\n", + inductlen, inductstep.ez->numCnfVariables(), inductstep.ez->numCnfClauses()); + + if (!inductstep.solve(inductstep.ez->NOT(property))) { + if (inductstep.gotTimeout) + goto timeout; + log("Induction step proven: SUCCESS!\n"); + print_qed(); + goto tip_success; + } + + log("Induction step failed. Incrementing induction length.\n"); + inductstep.ez->assume(property); + inductstep.print_model(); } - - log("Induction step failed. Incrementing induction length.\n"); - inductstep.ez.assume(property); - inductstep.print_model(); } } + if (tempinduct_baseonly) { + log("\nReached maximum number of time steps -> proved base case for %d steps: SUCCESS!\n", maxsteps); + goto tip_success; + } + log("\nReached maximum number of time steps -> proof failed.\n"); if(!vcd_file_name.empty()) inductstep.dump_model_to_vcd(vcd_file_name); + if(!json_file_name.empty()) + inductstep.dump_model_to_json(json_file_name); print_proof_failed(); tip_failed: @@ -1349,6 +1571,7 @@ struct SatPass : public Pass { SatHelper sathelper(design, module, enable_undef); sathelper.sets = sets; + sathelper.set_assumes = set_assumes; sathelper.prove = prove; sathelper.prove_x = prove_x; sathelper.prove_asserts = prove_asserts; @@ -1372,7 +1595,7 @@ struct SatPass : public Pass { if (seq_len == 0) { sathelper.setup(); if (sathelper.prove.size() || sathelper.prove_x.size() || sathelper.prove_asserts) - sathelper.ez.assume(sathelper.ez.NOT(sathelper.setup_proof())); + sathelper.ez->assume(sathelper.ez->NOT(sathelper.setup_proof())); } else { std::vector<int> prove_bits; for (int timestep = 1; timestep <= seq_len; timestep++) { @@ -1382,7 +1605,7 @@ struct SatPass : public Pass { prove_bits.push_back(sathelper.setup_proof(timestep)); } if (sathelper.prove.size() || sathelper.prove_x.size() || sathelper.prove_asserts) - sathelper.ez.assume(sathelper.ez.NOT(sathelper.ez.expression(ezSAT::OpAnd, prove_bits))); + sathelper.ez->assume(sathelper.ez->NOT(sathelper.ez->expression(ezSAT::OpAnd, prove_bits))); sathelper.setup_init(); } sathelper.generate_model(); @@ -1396,7 +1619,7 @@ struct SatPass : public Pass { log("Dumping CNF to file `%s'.\n", cnf_file_name.c_str()); cnf_file_name.clear(); - sathelper.ez.printDIMACS(f, false); + sathelper.ez->printDIMACS(f, false); fclose(f); } @@ -1404,7 +1627,7 @@ struct SatPass : public Pass { rerun_solver: log("\nSolving problem with %d variables and %d clauses..\n", - sathelper.ez.numCnfVariables(), sathelper.ez.numCnfClauses()); + sathelper.ez->numCnfVariables(), sathelper.ez->numCnfClauses()); if (sathelper.solve()) { @@ -1424,6 +1647,8 @@ struct SatPass : public Pass { if(!vcd_file_name.empty()) sathelper.dump_model_to_vcd(vcd_file_name); + if(!json_file_name.empty()) + sathelper.dump_model_to_json(json_file_name); if (loopcount != 0) { loopcount--, rerun_counter++; @@ -1487,5 +1712,5 @@ struct SatPass : public Pass { } } } SatPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index d8a433164..96fa0d92a 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -5,17 +5,35 @@ OBJS += passes/techmap/dfflibmap.o OBJS += passes/techmap/maccmap.o OBJS += passes/techmap/libparse.o +ifeq ($(ENABLE_ABC),1) +OBJS += passes/techmap/abc.o +ifneq ($(ABCEXTERNAL),) +passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' +endif +endif + ifneq ($(SMALL),1) OBJS += passes/techmap/iopadmap.o OBJS += passes/techmap/hilomap.o OBJS += passes/techmap/extract.o OBJS += passes/techmap/alumacc.o OBJS += passes/techmap/dff2dffe.o +OBJS += passes/techmap/dffinit.o +OBJS += passes/techmap/pmuxtree.o +OBJS += passes/techmap/muxcover.o +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 +OBJS += passes/techmap/shregmap.o +OBJS += passes/techmap/deminout.o endif GENFILES += passes/techmap/techmap.inc passes/techmap/techmap.inc: techlibs/common/techmap.v + $(Q) mkdir -p $(dir $@) $(P) echo "// autogenerated from $<" > $@.new $(Q) echo "static char stdcells_code[] = {" >> $@.new $(Q) od -v -td1 -An $< | $(SED) -e 's/[0-9][0-9]*/&,/g' >> $@.new @@ -24,9 +42,12 @@ passes/techmap/techmap.inc: techlibs/common/techmap.v passes/techmap/techmap.o: passes/techmap/techmap.inc +ifneq ($(CONFIG),emcc) TARGETS += yosys-filterlib$(EXE) EXTRA_OBJS += passes/techmap/filterlib.o yosys-filterlib$(EXE): passes/techmap/filterlib.o - $(P) $(CXX) -o yosys-filterlib$(EXE) $(LDFLAGS) $^ $(LDLIBS) + $(Q) mkdir -p $(dir $@) + $(P) $(LD) -o yosys-filterlib$(EXE) $(LDFLAGS) $^ $(LDLIBS) +endif diff --git a/passes/abc/abc.cc b/passes/techmap/abc.cc index 69da710f2..cc79296c5 100644 --- a/passes/abc/abc.cc +++ b/passes/techmap/abc.cc @@ -2,11 +2,11 @@ * 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 @@ -26,18 +26,20 @@ // http://www.ece.cmu.edu/~ee760/760docs/blif.pdf // [[CITE]] Kahn's Topological sorting algorithm -// Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558–562, doi:10.1145/368996.369025 +// Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558-562, doi:10.1145/368996.369025 // http://en.wikipedia.org/wiki/Topological_sorting -#define ABC_COMMAND_LIB "strash; scorr; ifraig; retime {D}; strash; dch -f; map {D}" -#define ABC_COMMAND_CTR "strash; scorr; ifraig; retime {D}; strash; dch -f; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" -#define ABC_COMMAND_LUT "strash; scorr; ifraig; retime; strash; dch -f; if" -#define ABC_COMMAND_DFL "strash; scorr; ifraig; retime; strash; dch -f; map" +#define ABC_COMMAND_LIB "strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f; map {D}" +#define ABC_COMMAND_CTR "strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_COMMAND_LUT "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; if; mfs" +#define ABC_COMMAND_SOP "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; cover {I} {P}" +#define ABC_COMMAND_DFL "strash; dc2; scorr; ifraig; retime -o; strash; dch -f; map" -#define ABC_FAST_COMMAND_LIB "retime {D}; map {D}" -#define ABC_FAST_COMMAND_CTR "retime {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" -#define ABC_FAST_COMMAND_LUT "retime; if" -#define ABC_FAST_COMMAND_DFL "retime; map" +#define ABC_FAST_COMMAND_LIB "retime -o {D}; map {D}" +#define ABC_FAST_COMMAND_CTR "retime -o {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_FAST_COMMAND_LUT "retime -o; if" +#define ABC_FAST_COMMAND_SOP "retime -o; cover -I {I} -P {P}" +#define ABC_FAST_COMMAND_DFL "retime -o; map" #include "kernel/register.h" #include "kernel/sigtools.h" @@ -56,7 +58,7 @@ # include <dirent.h> #endif -#include "blifparse.h" +#include "frontends/blif/blifparse.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -90,12 +92,17 @@ struct gate_t RTLIL::SigBit bit; }; +bool map_mux4; +bool map_mux8; +bool map_mux16; + bool markgroups; int map_autoidx; 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; @@ -587,8 +594,9 @@ 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, - bool keepff, std::string delay_target, bool fast_mode, const std::vector<RTLIL::Cell*> &cells, bool show_tempdir) + 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, std::string sop_inputs, std::string sop_products, bool fast_mode, + const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode) { module = current_module; map_autoidx = autoidx++; @@ -611,7 +619,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (!cleanup) tempdir_name[0] = tempdir_name[4] = '_'; tempdir_name = make_temp_dir(tempdir_name); - log_header("Extracting gate netlist of module `%s' to `%s/input.blif'..\n", + log_header(design, "Extracting gate netlist of module `%s' to `%s/input.blif'..\n", module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str()); std::string abc_script = stringf("read_blif %s/input.blif; ", tempdir_name.c_str()); @@ -621,7 +629,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()); @@ -637,16 +645,30 @@ 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()) { + bool all_luts_cost_same = true; + for (int this_cost : lut_costs) + if (this_cost != lut_costs.front()) + all_luts_cost_same = false; abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; - else if (!liberty_file.empty()) + if (all_luts_cost_same && !fast_mode) + abc_script += "; lutpack"; + } 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); + else if (sop_mode) + abc_script += fast_mode ? ABC_FAST_COMMAND_SOP : ABC_COMMAND_SOP; else abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL; for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3); + for (size_t pos = abc_script.find("{I}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) + abc_script = abc_script.substr(0, pos) + sop_inputs + abc_script.substr(pos+3); + + for (size_t pos = abc_script.find("{P}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) + abc_script = abc_script.substr(0, pos) + sop_products + abc_script.substr(pos+3); + abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str()); abc_script = add_echos_to_abc_cmd(abc_script); @@ -823,7 +845,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (count_output > 0) { - log_header("Executing ABC.\n"); + log_header(design, "Executing ABC.\n"); buffer = stringf("%s/stdcells.genlib", tempdir_name.c_str()); f = fopen(buffer.c_str(), "wt"); @@ -833,28 +855,43 @@ 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) + fprintf(f, "GATE MUX8 %d Y=(!S*!T*!U*A)+(S*!T*!U*B)+(!S*T*!U*C)+(S*T*!U*D)+(!S*!T*U*E)+(S*!T*U*F)+(!S*T*U*G)+(S*T*U*H); PIN * UNKNOWN 1 999 1 0 1 0\n", 4*get_cell_cost("$_MUX_")); + if (map_mux16) + 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); } @@ -867,16 +904,18 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); buffer = stringf("%s/%s", tempdir_name.c_str(), "output.blif"); - f = fopen(buffer.c_str(), "rt"); - if (f == NULL) + std::ifstream ifs; + ifs.open(buffer); + 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; - RTLIL::Design *mapped_design = abc_parse_blif(f, builtin_lib ? "\\DFF" : "\\_dff_"); + bool builtin_lib = liberty_file.empty(); + RTLIL::Design *mapped_design = new RTLIL::Design; + parse_blif(mapped_design, ifs, builtin_lib ? "\\DFF" : "\\_dff_", false, sop_mode); - fclose(f); + ifs.close(); - log_header("Re-integrating ABC results.\n"); + log_header(design, "Re-integrating ABC results.\n"); RTLIL::Module *mapped_mod = mapped_design->modules_["\\netlist"]; if (mapped_mod == NULL) log_error("ABC output file does not contain a module `netlist'.\n"); @@ -888,10 +927,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin } std::map<std::string, int> cell_stats; - if (builtin_lib) + for (auto c : mapped_mod->cells()) { - for (auto &it : mapped_mod->cells_) { - RTLIL::Cell *c = it.second; + if (builtin_lib) + { cell_stats[RTLIL::unescape_id(c->type)]++; if (c->type == "\\ZERO" || c->type == "\\ONE") { RTLIL::SigSig conn; @@ -934,61 +973,86 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin design->select(module, cell); continue; } - if (c->type == "\\AOI3" || c->type == "\\OAI3") { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); + if (c->type == "\\MUX4") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX4_"); if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)])); cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)])); + cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)])); + cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)])); + cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)])); cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); design->select(module, cell); continue; } - if (c->type == "\\AOI4" || c->type == "\\OAI4") { - RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); + if (c->type == "\\MUX8") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX8_"); if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)])); cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)])); cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)])); + cell->setPort("\\E", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\E").as_wire()->name)])); + cell->setPort("\\F", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\F").as_wire()->name)])); + cell->setPort("\\G", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\G").as_wire()->name)])); + cell->setPort("\\H", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\H").as_wire()->name)])); + cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)])); + cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)])); + cell->setPort("\\U", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\U").as_wire()->name)])); cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); design->select(module, cell); continue; } - if (c->type == "\\DFF") { - log_assert(clk_sig.size() == 1); - RTLIL::Cell *cell; - if (en_sig.size() == 0) { - cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_"); - } else { - log_assert(en_sig.size() == 1); - cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N')); - cell->setPort("\\E", en_sig); - } + if (c->type == "\\MUX16") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX16_"); if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); + cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)])); + cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)])); cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)])); - cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)])); - cell->setPort("\\C", clk_sig); + cell->setPort("\\E", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\E").as_wire()->name)])); + cell->setPort("\\F", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\F").as_wire()->name)])); + cell->setPort("\\G", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\G").as_wire()->name)])); + cell->setPort("\\H", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\H").as_wire()->name)])); + cell->setPort("\\I", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\I").as_wire()->name)])); + cell->setPort("\\J", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\J").as_wire()->name)])); + cell->setPort("\\K", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\K").as_wire()->name)])); + cell->setPort("\\L", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\L").as_wire()->name)])); + cell->setPort("\\M", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\M").as_wire()->name)])); + cell->setPort("\\N", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\N").as_wire()->name)])); + cell->setPort("\\O", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\O").as_wire()->name)])); + cell->setPort("\\P", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\P").as_wire()->name)])); + cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)])); + cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)])); + cell->setPort("\\U", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\U").as_wire()->name)])); + cell->setPort("\\V", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\V").as_wire()->name)])); + cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); design->select(module, cell); continue; } - log_abort(); - } - } - else - { - for (auto &it : mapped_mod->cells_) - { - RTLIL::Cell *c = it.second; - cell_stats[RTLIL::unescape_id(c->type)]++; - if (c->type == "\\_const0_" || c->type == "\\_const1_") { - RTLIL::SigSig conn; - conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->connections().begin()->second.as_wire()->name)]); - conn.second = RTLIL::SigSpec(c->type == "\\_const0_" ? 0 : 1, 1); - module->connect(conn); + if (c->type == "\\AOI3" || c->type == "\\OAI3") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); + cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)])); + cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)])); + cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); + design->select(module, cell); + continue; + } + if (c->type == "\\AOI4" || c->type == "\\OAI4") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)])); + cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)])); + cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)])); + cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)])); + cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)])); + design->select(module, cell); continue; } - if (c->type == "\\_dff_") { + if (c->type == "\\DFF") { log_assert(clk_sig.size() == 1); RTLIL::Cell *cell; if (en_sig.size() == 0) { @@ -1005,21 +1069,57 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin design->select(module, cell); continue; } - RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type); - if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; - cell->parameters = c->parameters; - for (auto &conn : c->connections()) { - RTLIL::SigSpec newsig; - for (auto &c : conn.second.chunks()) { - if (c.width == 0) - continue; - log_assert(c.width == 1); - newsig.append(module->wires_[remap_name(c.wire->name)]); - } - cell->setPort(conn.first, newsig); + } + + cell_stats[RTLIL::unescape_id(c->type)]++; + + if (c->type == "\\_const0_" || c->type == "\\_const1_") { + RTLIL::SigSig conn; + conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->connections().begin()->second.as_wire()->name)]); + conn.second = RTLIL::SigSpec(c->type == "\\_const0_" ? 0 : 1, 1); + module->connect(conn); + continue; + } + + if (c->type == "\\_dff_") { + log_assert(clk_sig.size() == 1); + RTLIL::Cell *cell; + if (en_sig.size() == 0) { + cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_"); + } else { + log_assert(en_sig.size() == 1); + cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N')); + cell->setPort("\\E", en_sig); } + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)])); + cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)])); + cell->setPort("\\C", clk_sig); design->select(module, cell); + continue; + } + + if (c->type == "$lut" && GetSize(c->getPort("\\A")) == 1 && c->getParam("\\LUT").as_int() == 2) { + SigSpec my_a = module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]; + SigSpec my_y = module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]; + module->connect(my_y, my_a); + continue; + } + + RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + cell->parameters = c->parameters; + for (auto &conn : c->connections()) { + RTLIL::SigSpec newsig; + for (auto &c : conn.second.chunks()) { + if (c.width == 0) + continue; + log_assert(c.width == 1); + newsig.append(module->wires_[remap_name(c.wire->name)]); + } + cell->setPort(conn.first, newsig); } + design->select(module, cell); } for (auto conn : mapped_mod->connections()) { @@ -1081,14 +1181,18 @@ struct AbcPass : public Pass { log("library to a target architecture.\n"); log("\n"); log(" -exe <command>\n"); - log(" use the specified command name instead of \"yosys-abc\" to execute ABC.\n"); +#ifdef ABCEXTERNAL + log(" use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n"); +#else + log(" use the specified command instead of \"<yosys-bindir>/yosys-abc\" to execute ABC.\n"); +#endif log(" This can e.g. be used to call a specific version of ABC or a wrapper.\n"); log("\n"); log(" -script <file>\n"); log(" use the specified ABC script file instead of the default script.\n"); log("\n"); log(" if <file> starts with a plus sign (+), then the rest of the filename\n"); - log(" string is interprated as the command string to be passed to ABC. the\n"); + log(" string is interpreted as the command string to be passed to ABC. The\n"); log(" leading plus sign is removed and all commas (,) in the string are\n"); log(" replaced with blanks before the string is passed to ABC.\n"); log("\n"); @@ -1100,9 +1204,15 @@ struct AbcPass : public Pass { log(" for -liberty with -constr:\n"); log("%s\n", fold_abc_cmd(ABC_COMMAND_CTR).c_str()); log("\n"); - log(" for -lut:\n"); + log(" for -lut/-luts (only one LUT size):\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT "; lutpack").c_str()); + log("\n"); + log(" for -lut/-luts (different LUT sizes):\n"); log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT).c_str()); log("\n"); + log(" for -sop:\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_SOP).c_str()); + log("\n"); log(" otherwise:\n"); log("%s\n", fold_abc_cmd(ABC_COMMAND_DFL).c_str()); log("\n"); @@ -1116,9 +1226,12 @@ struct AbcPass : public Pass { log(" for -liberty with -constr:\n"); log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_CTR).c_str()); log("\n"); - log(" for -lut:\n"); + log(" for -lut/-luts:\n"); log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LUT).c_str()); log("\n"); + log(" for -sop:\n"); + log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_SOP).c_str()); + log("\n"); log(" otherwise:\n"); log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_DFL).c_str()); log("\n"); @@ -1141,6 +1254,14 @@ struct AbcPass : public Pass { log(" set delay target. the string {D} in the default scripts above is\n"); log(" replaced by this option when used, and an empty string otherwise.\n"); log("\n"); + log(" -I <num>\n"); + log(" maximum number of SOP inputs.\n"); + log(" (replaces {I} in the default scripts above)\n"); + log("\n"); + log(" -P <num>\n"); + log(" maximum number of SOP products.\n"); + log(" (replaces {P} in the default scripts above)\n"); + log("\n"); log(" -lut <width>\n"); log(" generate netlist using luts of (max) the specified width.\n"); log("\n"); @@ -1150,6 +1271,22 @@ 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(" -sop\n"); + log(" map to sum-of-product cells and inverters\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"); @@ -1161,7 +1298,7 @@ struct AbcPass : public Pass { log("\n"); log(" -keepff\n"); log(" set the \"keep\" attribute on flip-flop output wires. (and thus preserve\n"); - log(" them, for example for equivialence checking.)\n"); + log(" them, for example for equivalence checking.)\n"); log("\n"); log(" -nocleanup\n"); log(" when this option is used, the temporary files created by this pass\n"); @@ -1187,20 +1324,32 @@ struct AbcPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing ABC pass (technology mapping using ABC).\n"); + log_header(design, "Executing ABC pass (technology mapping using ABC).\n"); log_push(); +#ifdef ABCEXTERNAL + std::string exe_file = ABCEXTERNAL; +#else std::string exe_file = proc_self_dirname() + "yosys-abc"; - std::string script_file, liberty_file, constr_file, clk_str, delay_target; +#endif + std::string script_file, liberty_file, constr_file, clk_str; + std::string delay_target, sop_inputs, sop_products; bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; - bool show_tempdir = false; - int lut_mode = 0, lut_mode2 = 0; + bool show_tempdir = false, sop_mode = false; + vector<int> lut_costs; markgroups = false; + map_mux4 = false; + map_mux8 = false; + map_mux16 = false; + enabled_gates.clear(); + #ifdef _WIN32 +#ifndef ABCEXTERNAL if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe")) exe_file = proc_self_dirname() + "..\\yosys-abc"; #endif +#endif size_t argidx; char pwd [PATH_MAX]; @@ -1216,19 +1365,19 @@ struct AbcPass : public Pass { } if (arg == "-script" && argidx+1 < args.size()) { script_file = args[++argidx]; - if (!script_file.empty() && script_file[0] != '/' && script_file[0] != '+') + if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') script_file = std::string(pwd) + "/" + script_file; continue; } if (arg == "-liberty" && argidx+1 < args.size()) { liberty_file = args[++argidx]; - if (!liberty_file.empty() && liberty_file[0] != '/') + if (!liberty_file.empty() && !is_absolute_path(liberty_file)) liberty_file = std::string(pwd) + "/" + liberty_file; continue; } if (arg == "-constr" && argidx+1 < args.size()) { constr_file = args[++argidx]; - if (!constr_file.empty() && constr_file[0] != '/') + if (!constr_file.empty() && !is_absolute_path(constr_file)) constr_file = std::string(pwd) + "/" + constr_file; continue; } @@ -1236,9 +1385,18 @@ struct AbcPass : public Pass { delay_target = "-D " + args[++argidx]; continue; } + if (arg == "-I" && argidx+1 < args.size()) { + sop_inputs = "-I " + args[++argidx]; + continue; + } + if (arg == "-P" && argidx+1 < args.size()) { + sop_products = "-P " + args[++argidx]; + continue; + } 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()); @@ -1246,6 +1404,62 @@ 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 == "-sop") { + sop_mode = true; + continue; + } + if (arg == "-mux4") { + map_mux4 = true; + continue; + } + if (arg == "-mux8") { + map_mux8 = true; + continue; + } + if (arg == "-mux16") { + 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") { @@ -1281,7 +1495,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"); @@ -1290,7 +1504,8 @@ 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, sop_inputs, sop_products, fast_mode, mod->selected_cells(), show_tempdir, sop_mode); else { assign_map.set(mod); @@ -1303,7 +1518,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; @@ -1423,7 +1638,7 @@ struct AbcPass : public Pass { assigned_cells_reverse[cell] = key; } - log_header("Summary of detected clock domains:\n"); + log_header(design, "Summary of detected clock domains:\n"); for (auto &it : assigned_cells) log(" %d cells in clk=%s%s, en=%s%s\n", GetSize(it.second), std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)), @@ -1434,8 +1649,8 @@ 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, - !clk_sig.empty(), "$", keepff, delay_target, fast_mode, it.second, show_tempdir); + abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", + keepff, delay_target, sop_inputs, sop_products, fast_mode, it.second, show_tempdir, sop_mode); assign_map.set(mod); } } @@ -1447,5 +1662,5 @@ struct AbcPass : public Pass { log_pop(); } } AbcPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/aigmap.cc b/passes/techmap/aigmap.cc new file mode 100644 index 000000000..b9ac7aded --- /dev/null +++ b/passes/techmap/aigmap.cc @@ -0,0 +1,149 @@ +/* + * 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/cellaigs.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct AigmapPass : public Pass { + AigmapPass() : Pass("aigmap", "map logic to and-inverter-graph circuit") { } + virtual void help() + { + log("\n"); + log(" aigmap [options] [selection]\n"); + log("\n"); + log("Replace all logic cells with circuits made of only $_AND_ and\n"); + log("$_NOT_ cells.\n"); + log("\n"); + log(" -nand\n"); + log(" Enable creation of $_NAND_ cells\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + bool nand_mode = false; + + log_header(design, "Executing AIGMAP pass (map logic to AIG).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-nand") { + nand_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + vector<Cell*> replaced_cells; + int not_replaced_count = 0; + dict<IdString, int> stat_replaced; + dict<IdString, int> stat_not_replaced; + int orig_num_cells = GetSize(module->cells()); + + for (auto cell : module->selected_cells()) + { + Aig aig(cell); + + if (cell->type == "$_AND_" || cell->type == "$_NOT_") + aig.name.clear(); + + if (nand_mode && cell->type == "$_NAND_") + aig.name.clear(); + + if (aig.name.empty()) { + not_replaced_count++; + stat_not_replaced[cell->type]++; + continue; + } + + vector<SigBit> sigs; + dict<pair<int, int>, SigBit> and_cache; + + for (int node_idx = 0; node_idx < GetSize(aig.nodes); node_idx++) + { + SigBit bit; + auto &node = aig.nodes[node_idx]; + + if (node.portbit >= 0) { + bit = cell->getPort(node.portname)[node.portbit]; + } else if (node.left_parent < 0 && node.right_parent < 0) { + bit = node.inverter ? State::S1 : State::S0; + goto skip_inverter; + } else { + SigBit A = sigs.at(node.left_parent); + SigBit B = sigs.at(node.right_parent); + if (nand_mode && node.inverter) { + bit = module->NandGate(NEW_ID, A, B); + goto skip_inverter; + } else { + pair<int, int> key(node.left_parent, node.right_parent); + if (and_cache.count(key)) + bit = and_cache.at(key); + else + bit = module->AndGate(NEW_ID, A, B); + } + } + + if (node.inverter) + bit = module->NotGate(NEW_ID, bit); + + skip_inverter: + for (auto &op : node.outports) + module->connect(cell->getPort(op.first)[op.second], bit); + + sigs.push_back(bit); + } + + replaced_cells.push_back(cell); + stat_replaced[cell->type]++; + } + + if (not_replaced_count == 0 && replaced_cells.empty()) + continue; + + log("Module %s: replaced %d cells with %d new cells, skipped %d cells.\n", log_id(module), + GetSize(replaced_cells), GetSize(module->cells()) - orig_num_cells, not_replaced_count); + + if (!stat_replaced.empty()) { + stat_replaced.sort(); + log(" replaced %d cell types:\n", GetSize(stat_replaced)); + for (auto &it : stat_replaced) + log("%8d %s\n", it.second, log_id(it.first)); + } + + if (!stat_not_replaced.empty()) { + stat_not_replaced.sort(); + log(" not replaced %d cell types:\n", GetSize(stat_not_replaced)); + for (auto &it : stat_not_replaced) + log("%8d %s\n", it.second, log_id(it.first)); + } + + for (auto cell : replaced_cells) + module->remove(cell); + } + } +} AigmapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc index dcffed94d..9f6dd02d0 100644 --- a/passes/techmap/alumacc.cc +++ b/passes/techmap/alumacc.cc @@ -2,11 +2,11 @@ * 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 @@ -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; @@ -98,9 +98,9 @@ struct AlumaccWorker } }; - std::map<RTLIL::SigBit, int> bit_users; - std::map<RTLIL::SigSpec, maccnode_t*> sig_macc; - std::map<RTLIL::SigSig, std::set<alunode_t*>> sig_alu; + dict<RTLIL::SigBit, int> bit_users; + dict<RTLIL::SigSpec, maccnode_t*> sig_macc; + dict<RTLIL::SigSig, pool<alunode_t*, hash_ptr_ops>> sig_alu; int macc_counter, alu_counter; AlumaccWorker(RTLIL::Module *module) : module(module), sigmap(module) @@ -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")) { @@ -215,7 +215,7 @@ struct AlumaccWorker { while (1) { - std::set<maccnode_t*> delete_nodes; + pool<maccnode_t*, hash_ptr_ops> delete_nodes; for (auto &it : sig_macc) { @@ -267,7 +267,7 @@ struct AlumaccWorker void macc_to_alu() { - std::set<maccnode_t*> delete_nodes; + pool<maccnode_t*, hash_ptr_ops> delete_nodes; for (auto &it : sig_macc) { @@ -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); @@ -544,7 +544,7 @@ struct AlumaccPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing ALUMACC pass (create $alu and $macc cells).\n"); + log_header(design, "Executing ALUMACC pass (create $alu and $macc cells).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -563,5 +563,5 @@ struct AlumaccPass : public Pass { } } } AlumaccPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/deminout.cc b/passes/techmap/deminout.cc new file mode 100644 index 000000000..ed4e45762 --- /dev/null +++ b/passes/techmap/deminout.cc @@ -0,0 +1,116 @@ +/* + * 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 DeminoutPass : public Pass { + DeminoutPass() : Pass("deminout", "demote inout ports to input or output") { } + virtual void help() + { + log("\n"); + log(" deminout [options] [selection]\n"); + log("\n"); + log("\"Demote\" inout ports to input or output ports, if possible.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing DEMINOUT pass (demote inout ports to input or output).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-bits") { + // flag_bits = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + bool keep_running = true; + + while (keep_running) + { + keep_running = false; + + for (auto module : design->selected_modules()) + { + SigMap sigmap(module); + pool<SigBit> bits_written, bits_used, bits_inout; + dict<SigBit, int> bits_numports; + + for (auto wire : module->wires()) + if (wire->port_id) + for (auto bit : sigmap(wire)) + bits_numports[bit]++; + + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) + { + bool cellport_out = cell->output(conn.first) || !cell->known(); + bool cellport_in = cell->input(conn.first) || !cell->known(); + + if (cellport_out && cellport_in) + for (auto bit : sigmap(conn.second)) + bits_inout.insert(bit); + + if (cellport_out) + for (auto bit : sigmap(conn.second)) + bits_written.insert(bit); + + if (cellport_in) + for (auto bit : sigmap(conn.second)) + bits_used.insert(bit); + } + + for (auto wire : module->selected_wires()) + if (wire->port_input && wire->port_output) + { + bool new_input = false; + bool new_output = false; + + for (auto bit : sigmap(wire)) + { + if (bits_numports[bit] > 1 || bits_inout.count(bit)) + new_input = true, new_output = true; + + if (bits_written.count(bit)) + new_output = true; + else if (bits_used.count(bit)) + new_input = true; + } + + if (new_input != new_output) { + log("Demoting inout port %s.%s to %s.\n", log_id(module), log_id(wire), new_input ? "input" : "output"); + wire->port_input = new_input; + wire->port_output = new_output; + keep_running = true; + } + } + } + } + } +} DeminoutPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc index 17549bd06..1b8920bb7 100644 --- a/passes/techmap/dff2dffe.cc +++ b/passes/techmap/dff2dffe.cc @@ -2,11 +2,11 @@ * 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 @@ -27,12 +27,12 @@ PRIVATE_NAMESPACE_BEGIN struct Dff2dffeWorker { + const dict<IdString, IdString> &direct_dict; + RTLIL::Module *module; SigMap sigmap; CellTypes ct; - RTLIL::IdString direct_to; - typedef std::pair<RTLIL::Cell*, int> cell_int_t; std::map<RTLIL::SigBit, cell_int_t> bit2mux; std::vector<RTLIL::Cell*> dff_cells; @@ -42,8 +42,8 @@ struct Dff2dffeWorker typedef std::set<pattern_t> patterns_t; - Dff2dffeWorker(RTLIL::Module *module, RTLIL::IdString direct_from, RTLIL::IdString direct_to) : - module(module), sigmap(module), ct(module->design), direct_to(direct_to) + Dff2dffeWorker(RTLIL::Module *module, const dict<IdString, IdString> &direct_dict) : + direct_dict(direct_dict), module(module), sigmap(module), ct(module->design) { for (auto wire : module->wires()) { if (wire->port_output) @@ -57,11 +57,11 @@ struct Dff2dffeWorker for (int i = 0; i < GetSize(sig_y); i++) bit2mux[sig_y[i]] = cell_int_t(cell, i); } - if (direct_to.empty()) { + if (direct_dict.empty()) { if (cell->type == "$dff" || cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_") dff_cells.push_back(cell); } else { - if (cell->type == direct_from) + if (direct_dict.count(cell->type)) dff_cells.push_back(cell); } for (auto conn : cell->connections()) { @@ -206,10 +206,10 @@ struct Dff2dffeWorker new_sig_d.append(sig_d[i]); new_sig_q.append(sig_q[i]); } - if (!direct_to.empty()) { - log(" converting %s cell %s to %s for %s -> %s.\n", log_id(dff_cell->type), log_id(dff_cell), log_id(direct_to), log_signal(new_sig_d), log_signal(new_sig_q)); + if (!direct_dict.empty()) { + log(" converting %s cell %s to %s for %s -> %s.\n", log_id(dff_cell->type), log_id(dff_cell), log_id(direct_dict.at(dff_cell->type)), log_signal(new_sig_d), log_signal(new_sig_q)); dff_cell->setPort("\\E", make_patterns_logic(it.first, true)); - dff_cell->type = direct_to; + dff_cell->type = direct_dict.at(dff_cell->type); } else if (dff_cell->type == "$dff") { RTLIL::Cell *new_cell = module->addDffe(NEW_ID, dff_cell->getPort("\\CLK"), make_patterns_logic(it.first, false), @@ -222,7 +222,7 @@ struct Dff2dffeWorker } } - if (!direct_to.empty()) + if (!direct_dict.empty()) return; if (remaining_indices.empty()) { @@ -243,9 +243,11 @@ struct Dff2dffeWorker void run() { - log("Transforming $dff to $dffe cells in module %s:\n", log_id(module)); - for (auto dff_cell : dff_cells) + log("Transforming FF to FF+Enable cells in module %s:\n", log_id(module)); + for (auto dff_cell : dff_cells) { + // log("Handling candidate %s:\n", log_id(dff_cell)); handle_dff_cell(dff_cell); + } } }; @@ -255,7 +257,7 @@ struct Dff2dffePass : public Pass { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" dff2dffe [selection]\n"); + log(" dff2dffe [options] [selection]\n"); log("\n"); log("This pass transforms $dff cells driven by a tree of multiplexers with one or\n"); log("more feedback paths to $dffe cells. It also works on gate-level cells such as\n"); @@ -271,16 +273,22 @@ struct Dff2dffePass : public Pass { log(" <external_gate_type> is the cell type name for a cell with an\n"); log(" identical interface to the <internal_gate_type>, except it\n"); log(" also has an high-active enable port 'E'.\n"); - log(" Usually <external_gate_type> is an intemediate cell type\n"); + log(" Usually <external_gate_type> is an intermediate cell type\n"); log(" that is then translated to the final type using 'techmap'.\n"); log("\n"); + log(" -direct-match <pattern>\n"); + log(" like -direct for all DFF cell types matching the expression.\n"); + log(" this will use $__DFFE_* as <external_gate_type> matching the\n"); + log(" internal gate type $_DFF_*_, except for $_DFF_[NP]_, which is\n"); + log(" converted to $_DFFE_[NP]_.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n"); + log_header(design, "Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n"); bool unmap_mode = false; - RTLIL::IdString direct_from, direct_to; + dict<IdString, IdString> direct_dict; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -289,14 +297,38 @@ struct Dff2dffePass : public Pass { continue; } if (args[argidx] == "-direct" && argidx + 2 < args.size()) { - direct_from = RTLIL::escape_id(args[++argidx]); - direct_to = RTLIL::escape_id(args[++argidx]); + string direct_from = RTLIL::escape_id(args[++argidx]); + string direct_to = RTLIL::escape_id(args[++argidx]); + direct_dict[direct_from] = direct_to; + continue; + } + if (args[argidx] == "-direct-match" && argidx + 1 < args.size()) { + bool found_match = false; + const char *pattern = args[++argidx].c_str(); + if (patmatch(pattern, "$_DFF_P_" )) found_match = true, direct_dict["$_DFF_P_" ] = "$_DFFE_PP_"; + if (patmatch(pattern, "$_DFF_N_" )) found_match = true, direct_dict["$_DFF_N_" ] = "$_DFFE_NP_"; + if (patmatch(pattern, "$_DFF_NN0_")) found_match = true, direct_dict["$_DFF_NN0_"] = "$__DFFE_NN0"; + if (patmatch(pattern, "$_DFF_NN1_")) found_match = true, direct_dict["$_DFF_NN1_"] = "$__DFFE_NN1"; + if (patmatch(pattern, "$_DFF_NP0_")) found_match = true, direct_dict["$_DFF_NP0_"] = "$__DFFE_NP0"; + if (patmatch(pattern, "$_DFF_NP1_")) found_match = true, direct_dict["$_DFF_NP1_"] = "$__DFFE_NP1"; + if (patmatch(pattern, "$_DFF_PN0_")) found_match = true, direct_dict["$_DFF_PN0_"] = "$__DFFE_PN0"; + if (patmatch(pattern, "$_DFF_PN1_")) found_match = true, direct_dict["$_DFF_PN1_"] = "$__DFFE_PN1"; + if (patmatch(pattern, "$_DFF_PP0_")) found_match = true, direct_dict["$_DFF_PP0_"] = "$__DFFE_PP0"; + if (patmatch(pattern, "$_DFF_PP1_")) found_match = true, direct_dict["$_DFF_PP1_"] = "$__DFFE_PP1"; + if (!found_match) + log_cmd_error("No cell types matched pattern '%s'.\n", pattern); continue; } break; } extra_args(args, argidx, design); + if (!direct_dict.empty()) { + log("Selected cell types for direct conversion:\n"); + for (auto &it : direct_dict) + log(" %s -> %s\n", log_id(it.first), log_id(it.second)); + } + for (auto mod : design->selected_modules()) if (!mod->has_processes_warn()) { @@ -328,10 +360,10 @@ struct Dff2dffePass : public Pass { continue; } - Dff2dffeWorker worker(mod, direct_from, direct_to); + Dff2dffeWorker worker(mod, direct_dict); worker.run(); } } } Dff2dffePass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc new file mode 100644 index 000000000..d737b3424 --- /dev/null +++ b/passes/techmap/dffinit.cc @@ -0,0 +1,135 @@ +/* + * 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 DffinitPass : public Pass { + DffinitPass() : Pass("dffinit", "set INIT param on FF cells") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" dffinit [options] [selection]\n"); + log("\n"); + log("This pass sets an FF cell parameter to the the initial value of the net it\n"); + log("drives. (This is primarily used in FPGA flows.)\n"); + log("\n"); + log(" -ff <cell_name> <output_port> <init_param>\n"); + log(" operate on the specified cell type. this option can be used\n"); + log(" multiple times.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing DFFINIT pass (set INIT param on FF cells).\n"); + + dict<IdString, dict<IdString, IdString>> ff_types; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-ff" && argidx+3 < args.size()) { + IdString cell_name = RTLIL::escape_id(args[++argidx]); + IdString output_port = RTLIL::escape_id(args[++argidx]); + IdString init_param = RTLIL::escape_id(args[++argidx]); + ff_types[cell_name][output_port] = init_param; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + SigMap sigmap(module); + dict<SigBit, State> init_bits; + pool<SigBit> cleanup_bits; + pool<SigBit> used_bits; + + for (auto wire : module->selected_wires()) { + if (wire->attributes.count("\\init")) { + Const value = wire->attributes.at("\\init"); + for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) + init_bits[sigmap(SigBit(wire, i))] = value[i]; + } + if (wire->port_output) + for (auto bit : sigmap(wire)) + used_bits.insert(bit); + } + + for (auto cell : module->selected_cells()) + { + for (auto it : cell->connections()) + if (!cell->known() || cell->input(it.first)) + for (auto bit : sigmap(it.second)) + used_bits.insert(bit); + + if (ff_types.count(cell->type) == 0) + continue; + + for (auto &it : ff_types[cell->type]) + { + if (!cell->hasPort(it.first)) + continue; + + SigSpec sig = sigmap(cell->getPort(it.first)); + Const value; + + if (cell->hasParam(it.second)) + value = cell->getParam(it.second); + + for (int i = 0; i < GetSize(sig); i++) { + if (init_bits.count(sig[i]) == 0) + continue; + while (GetSize(value.bits) <= i) + value.bits.push_back(State::S0); + value.bits[i] = init_bits.at(sig[i]); + cleanup_bits.insert(sig[i]); + } + + log("Setting %s.%s.%s (port=%s, net=%s) to %s.\n", log_id(module), log_id(cell), log_id(it.second), + log_id(it.first), log_signal(sig), log_signal(value)); + cell->setParam(it.second, value); + } + } + + for (auto wire : module->selected_wires()) + if (wire->attributes.count("\\init")) { + Const &value = wire->attributes.at("\\init"); + bool do_cleanup = true; + 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; + else if (value[i] != State::Sx) + do_cleanup = false; + } + if (do_cleanup) { + log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire)); + wire->attributes.erase("\\init"); + } + } + } + } +} DffinitPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index b0318a0b3..c8104fb7e 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -2,11 +2,11 @@ * 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 @@ -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> @@ -80,7 +80,7 @@ static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name, { if (cell == NULL || attr == NULL || attr->value.empty()) return false; - + std::string value = attr->value; for (size_t pos = value.find_first_of("\" \t()"); pos != std::string::npos; pos = value.find_first_of("\" \t()")) @@ -108,6 +108,7 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool LibertyAst *best_cell = NULL; std::map<std::string, char> best_cell_ports; int best_cell_pins = 0; + bool best_cell_noninv = false; double best_cell_area = 0; if (ast->id != "library") @@ -118,6 +119,10 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool if (cell->id != "cell" || cell->args.size() != 1) continue; + LibertyAst *dn = cell->find("dont_use"); + if (dn != NULL && dn->value == "true") + continue; + LibertyAst *ff = cell->find("ff"); if (ff == NULL) continue; @@ -151,6 +156,7 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool int num_pins = 0; bool found_output = false; + bool found_noninv_output = false; for (auto pin : cell->children) { if (pin->id != "pin" || pin->args.size() != 1) @@ -169,8 +175,16 @@ 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'; + if (cell_next_pol) + found_noninv_output = true; + found_output = true; + } else + if (value == ff->args[1]) { + this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q'; + if (!cell_next_pol) + found_noninv_output = true; found_output = true; } } @@ -179,7 +193,7 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool this_cell_ports[pin->args[0]] = 0; } - if (!found_output || (best_cell != NULL && num_pins > best_cell_pins)) + if (!found_output || (best_cell != NULL && (num_pins > best_cell_pins || (best_cell_noninv && !found_noninv_output)))) continue; if (best_cell != NULL && num_pins == best_cell_pins && area > best_cell_area) @@ -188,12 +202,14 @@ static void find_cell(LibertyAst *ast, std::string cell_type, bool clkpol, bool best_cell = cell; best_cell_pins = num_pins; best_cell_area = area; + best_cell_noninv = found_noninv_output; best_cell_ports.swap(this_cell_ports); continue_cell_loop:; } if (best_cell != NULL) { - log(" cell %s (pins=%d, area=%.2f) is a direct match for cell type %s.\n", best_cell->args[0].c_str(), best_cell_pins, best_cell_area, cell_type.c_str()); + log(" cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n", + best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str()); if (prepare_mode) { cell_mappings[cell_type].cell_name = cell_type; cell_mappings[cell_type].ports["C"] = 'C'; @@ -213,6 +229,7 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo LibertyAst *best_cell = NULL; std::map<std::string, char> best_cell_ports; int best_cell_pins = 0; + bool best_cell_noninv = false; double best_cell_area = 0; if (ast->id != "library") @@ -252,6 +269,7 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo int num_pins = 0; bool found_output = false; + bool found_noninv_output = false; for (auto pin : cell->children) { if (pin->id != "pin" || pin->args.size() != 1) @@ -270,8 +288,16 @@ 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'; + if (cell_next_pol) + found_noninv_output = true; + found_output = true; + } else + if (value == ff->args[1]) { + this_cell_ports[pin->args[0]] = cell_next_pol ? 'q' : 'Q'; + if (!cell_next_pol) + found_noninv_output = true; found_output = true; } } @@ -280,7 +306,7 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo this_cell_ports[pin->args[0]] = 0; } - if (!found_output || (best_cell != NULL && num_pins > best_cell_pins)) + if (!found_output || (best_cell != NULL && (num_pins > best_cell_pins || (best_cell_noninv && !found_noninv_output)))) continue; if (best_cell != NULL && num_pins == best_cell_pins && area > best_cell_area) @@ -289,12 +315,14 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo best_cell = cell; best_cell_pins = num_pins; best_cell_area = area; + best_cell_noninv = found_noninv_output; best_cell_ports.swap(this_cell_ports); continue_cell_loop:; } if (best_cell != NULL) { - log(" cell %s (pins=%d, area=%.2f) is a direct match for cell type %s.\n", best_cell->args[0].c_str(), best_cell_pins, best_cell_area, cell_type.c_str()); + log(" cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n", + best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str()); if (prepare_mode) { cell_mappings[cell_type].cell_name = cell_type; cell_mappings[cell_type].ports["C"] = 'C'; @@ -406,14 +434,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; @@ -427,6 +483,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') { @@ -435,7 +497,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'))]; @@ -444,7 +513,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); } @@ -476,7 +547,7 @@ struct DfflibmapPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n"); + log_header(design, "Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n"); std::string liberty_file; bool prepare_mode = false; @@ -487,6 +558,7 @@ struct DfflibmapPass : public Pass { std::string arg = args[argidx]; if (arg == "-liberty" && argidx+1 < args.size()) { liberty_file = args[++argidx]; + rewrite_filename(liberty_file); continue; } if (arg == "-prepare") { @@ -558,7 +630,16 @@ struct DfflibmapPass : public Pass { map_sr_to_arst("$_DFFSR_PNN_", "$_DFF_PN1_"); 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(); @@ -569,5 +650,5 @@ struct DfflibmapPass : public Pass { cell_mappings.clear(); } } DfflibmapPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/dffsr2dff.cc b/passes/techmap/dffsr2dff.cc new file mode 100644 index 000000000..0d4d53627 --- /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(design, "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 ff99040e1..71e29c60b 100644 --- a/passes/techmap/extract.cc +++ b/passes/techmap/extract.cc @@ -2,11 +2,11 @@ * 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 @@ -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)) @@ -361,7 +361,7 @@ struct ExtractPass : public Pass { log("\n"); log("This pass looks for subcircuits that are isomorphic to any of the modules\n"); log("in the given map file and replaces them with instances of this modules. The\n"); - log("map file can be a verilog source file (*.v) or an ilang file (*.il).\n"); + log("map file can be a Verilog source file (*.v) or an ilang file (*.il).\n"); log("\n"); log(" -map <map_file>\n"); log(" use the modules in this file as reference. This option can be used\n"); @@ -390,11 +390,11 @@ struct ExtractPass : public Pass { log(" match. This option can be used multiple times.\n"); log("\n"); log(" -swap <needle_type> <port1>,<port2>[,...]\n"); - log(" Register a set of swapable ports for a needle cell type.\n"); + log(" Register a set of swappable ports for a needle cell type.\n"); log(" This option can be used multiple times.\n"); log("\n"); log(" -perm <needle_type> <port1>,<port2>[,...] <portA>,<portB>[,...]\n"); - log(" Register a valid permutation of swapable ports for a needle\n"); + log(" Register a valid permutation of swappable ports for a needle\n"); log(" cell type. This option can be used multiple times.\n"); log("\n"); log(" -cell_attr <attribute_name>\n"); @@ -409,7 +409,7 @@ struct ExtractPass : public Pass { log(" -ignore_param <cell_type> <parameter_name>\n"); log(" Do not use this parameter when matching cells.\n"); log("\n"); - log("This pass does not operate on modules with uprocessed processes in it.\n"); + log("This pass does not operate on modules with unprocessed processes in it.\n"); log("(I.e. the 'proc' pass should be used first to convert processes to netlists.)\n"); log("\n"); log("This pass can also be used for mining for frequent subcircuits. In this mode\n"); @@ -442,7 +442,7 @@ struct ExtractPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing EXTRACT pass (map subcircuits to cells).\n"); + log_header(design, "Executing EXTRACT pass (map subcircuits to cells).\n"); log_push(); SubCircuitSolver solver; @@ -607,6 +607,7 @@ struct ExtractPass : public Pass { else { std::ifstream f; + rewrite_filename(filename); f.open(filename.c_str()); if (f.fail()) { delete map; @@ -626,7 +627,7 @@ struct ExtractPass : public Pass { std::map<std::string, RTLIL::Module*> needle_map, haystack_map; std::vector<RTLIL::Module*> needle_list; - log_header("Creating graphs for SubCircuit library.\n"); + log_header(design, "Creating graphs for SubCircuit library.\n"); if (!mine_mode) for (auto &mod_it : map->modules_) { @@ -649,11 +650,11 @@ struct ExtractPass : public Pass { haystack_map[graph_name] = mod_it.second; } } - + if (!mine_mode) { std::vector<SubCircuit::Solver::Result> results; - log_header("Running solver from SubCircuit library.\n"); + log_header(design, "Running solver from SubCircuit library.\n"); std::sort(needle_list.begin(), needle_list.end(), compareSortNeedleList); @@ -666,7 +667,7 @@ struct ExtractPass : public Pass { if (results.size() > 0) { - log_header("Substitute SubCircuits with cells.\n"); + log_header(design, "Substitute SubCircuits with cells.\n"); for (int i = 0; i < int(results.size()); i++) { auto &result = results[i]; @@ -687,7 +688,7 @@ struct ExtractPass : public Pass { { std::vector<SubCircuit::Solver::MineResult> results; - log_header("Running miner from SubCircuit library.\n"); + log_header(design, "Running miner from SubCircuit library.\n"); solver.mine(results, mine_cells_min, mine_cells_max, mine_min_freq, mine_limit_mod); map = new RTLIL::Design; @@ -736,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); @@ -746,6 +747,7 @@ struct ExtractPass : public Pass { } std::ofstream f; + rewrite_filename(mine_outfile); f.open(mine_outfile.c_str(), std::ofstream::trunc); if (f.fail()) log_error("Can't open output file `%s'.\n", mine_outfile.c_str()); @@ -757,5 +759,5 @@ struct ExtractPass : public Pass { log_pop(); } } ExtractPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/hilomap.cc b/passes/techmap/hilomap.cc index 9a14ffa3c..82cecac26 100644 --- a/passes/techmap/hilomap.cc +++ b/passes/techmap/hilomap.cc @@ -2,11 +2,11 @@ * 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 @@ -76,7 +76,7 @@ struct HilomapPass : public Pass { } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing HILOMAP pass (mapping to constant drivers).\n"); + log_header(design, "Executing HILOMAP pass (mapping to constant drivers).\n"); hicell_celltype = std::string(); hicell_portname = std::string(); @@ -119,5 +119,5 @@ struct HilomapPass : public Pass { } } } HilomapPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc index 75d02c828..4acbf7c0d 100644 --- a/passes/techmap/iopadmap.cc +++ b/passes/techmap/iopadmap.cc @@ -2,11 +2,11 @@ * 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 @@ -17,9 +17,8 @@ * */ -#include "kernel/register.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "kernel/sigtools.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -45,15 +44,26 @@ struct IopadmapPass : public Pass { log("the resulting cells to more sophisticated PAD cells.\n"); log("\n"); log(" -inpad <celltype> <portname>[:<portname>]\n"); - log(" Map module input ports to the given cell type with\n"); - log(" the given port name. if a 2nd portname is given, the\n"); + log(" Map module input ports to the given cell type with the\n"); + log(" given output port name. if a 2nd portname is given, the\n"); log(" signal is passed through the pad call, using the 2nd\n"); - log(" portname as output.\n"); + log(" portname as the port facing the module port.\n"); log("\n"); log(" -outpad <celltype> <portname>[:<portname>]\n"); log(" -inoutpad <celltype> <portname>[:<portname>]\n"); log(" Similar to -inpad, but for output and inout ports.\n"); log("\n"); + log(" -toutpad <celltype> <portname>:<portname>[:<portname>]\n"); + log(" Merges $_TBUF_ cells into the output pad cell. This takes precedence\n"); + log(" over the other -outpad cell. The first portname is the enable input\n"); + log(" of the tristate driver.\n"); + log("\n"); + log(" -tinoutpad <celltype> <portname>:<portname>:<portname>[:<portname>]\n"); + log(" Merges $_TBUF_ cells into the inout pad cell. This takes precedence\n"); + log(" over the other -inoutpad cell. The first portname is the enable input\n"); + log(" of the tristate driver and the 2nd portname is the internal output\n"); + log(" buffering the external signal.\n"); + log("\n"); log(" -widthparam <param_name>\n"); log(" Use the specified parameter name to set the port width.\n"); log("\n"); @@ -65,14 +75,18 @@ struct IopadmapPass : public Pass { log(" are wider. (the default behavior is to create word-wide\n"); log(" buffers using -widthparam to set the word size on the cell.)\n"); log("\n"); + log("Tristate PADS (-toutpad, -tinoutpad) always operate in -bits mode.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n"); + log_header(design, "Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n"); std::string inpad_celltype, inpad_portname, inpad_portname2; std::string outpad_celltype, outpad_portname, outpad_portname2; std::string inoutpad_celltype, inoutpad_portname, inoutpad_portname2; + std::string toutpad_celltype, toutpad_portname, toutpad_portname2, toutpad_portname3; + std::string tinoutpad_celltype, tinoutpad_portname, tinoutpad_portname2, tinoutpad_portname3, tinoutpad_portname4; std::string widthparam, nameparam; bool flag_bits = false; @@ -98,6 +112,21 @@ struct IopadmapPass : public Pass { split_portname_pair(inoutpad_portname, inoutpad_portname2); continue; } + if (arg == "-toutpad" && argidx+2 < args.size()) { + toutpad_celltype = args[++argidx]; + toutpad_portname = args[++argidx]; + split_portname_pair(toutpad_portname, toutpad_portname2); + split_portname_pair(toutpad_portname2, toutpad_portname3); + continue; + } + if (arg == "-tinoutpad" && argidx+2 < args.size()) { + tinoutpad_celltype = args[++argidx]; + tinoutpad_portname = args[++argidx]; + split_portname_pair(tinoutpad_portname, tinoutpad_portname2); + split_portname_pair(tinoutpad_portname2, tinoutpad_portname3); + split_portname_pair(tinoutpad_portname3, tinoutpad_portname4); + continue; + } if (arg == "-widthparam" && argidx+1 < args.size()) { widthparam = args[++argidx]; continue; @@ -114,21 +143,134 @@ struct IopadmapPass : public Pass { } extra_args(args, argidx, design); - for (auto &it : design->modules_) + for (auto module : design->selected_modules()) { - RTLIL::Module *module = it.second; - - if (!design->selected(module) || module->get_bool_attribute("\\blackbox")) - continue; + dict<IdString, pool<int>> skip_wires; - for (auto &it2 : module->wires_) + if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty()) { - RTLIL::Wire *wire = it2.second; + SigMap sigmap(module); + dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits; + + for (auto cell : module->cells()) + if (cell->type == "$_TBUF_") { + SigBit bit = sigmap(cell->getPort("\\Y").as_bit()); + tbuf_bits[bit].first = cell->name; + } + + for (auto cell : module->cells()) + for (auto port : cell->connections()) + for (auto bit : sigmap(port.second)) + if (tbuf_bits.count(bit)) + tbuf_bits.at(bit).second.insert(cell->name); + + for (auto wire : module->selected_wires()) + { + if (!wire->port_output) + continue; + + for (int i = 0; i < GetSize(wire); i++) + { + SigBit wire_bit(wire, i); + SigBit mapped_wire_bit = sigmap(wire_bit); + + if (tbuf_bits.count(mapped_wire_bit) == 0) + continue; + + auto &tbuf_cache = tbuf_bits.at(mapped_wire_bit); + Cell *tbuf_cell = module->cell(tbuf_cache.first); + + if (tbuf_cell == nullptr) + continue; - if (!wire->port_id || !design->selected(module, wire)) + SigBit en_sig = tbuf_cell->getPort("\\E").as_bit(); + SigBit data_sig = tbuf_cell->getPort("\\A").as_bit(); + + if (wire->port_input && !tinoutpad_celltype.empty()) + { + log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, tinoutpad_celltype.c_str()); + + Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(tinoutpad_celltype)); + Wire *owire = module->addWire(NEW_ID); + + cell->setPort(RTLIL::escape_id(tinoutpad_portname), en_sig); + cell->setPort(RTLIL::escape_id(tinoutpad_portname2), owire); + cell->setPort(RTLIL::escape_id(tinoutpad_portname3), data_sig); + cell->setPort(RTLIL::escape_id(tinoutpad_portname4), wire_bit); + cell->attributes["\\keep"] = RTLIL::Const(1); + + for (auto cn : tbuf_cache.second) { + auto c = module->cell(cn); + if (c == nullptr) + continue; + for (auto port : c->connections()) { + SigSpec sig = port.second; + bool newsig = false; + for (auto &bit : sig) + if (sigmap(bit) == mapped_wire_bit) { + bit = owire; + newsig = true; + } + if (newsig) + c->setPort(port.first, sig); + } + } + + + module->remove(tbuf_cell); + skip_wires[wire->name].insert(i); + continue; + } + + if (!wire->port_input && !toutpad_celltype.empty()) + { + log("Mapping port %s.%s[%d] using %s.\n", log_id(module), log_id(wire), i, toutpad_celltype.c_str()); + + Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(toutpad_celltype)); + + cell->setPort(RTLIL::escape_id(toutpad_portname), en_sig); + cell->setPort(RTLIL::escape_id(toutpad_portname2), data_sig); + cell->setPort(RTLIL::escape_id(toutpad_portname3), wire_bit); + cell->attributes["\\keep"] = RTLIL::Const(1); + + for (auto cn : tbuf_cache.second) { + auto c = module->cell(cn); + if (c == nullptr) + continue; + for (auto port : c->connections()) { + SigSpec sig = port.second; + bool newsig = false; + for (auto &bit : sig) + if (sigmap(bit) == mapped_wire_bit) { + bit = data_sig; + newsig = true; + } + if (newsig) + c->setPort(port.first, sig); + } + } + + module->remove(tbuf_cell); + skip_wires[wire->name].insert(i); + continue; + } + } + } + } + + for (auto wire : module->selected_wires()) + { + if (!wire->port_id) continue; std::string celltype, portname, portname2; + pool<int> skip_bit_indices; + + if (skip_wires.count(wire->name)) { + if (!flag_bits) + continue; + skip_bit_indices = skip_wires.at(wire->name); + } if (wire->port_input && !wire->port_output) { if (inpad_celltype.empty()) { @@ -170,12 +312,21 @@ struct IopadmapPass : public Pass { if (!portname2.empty()) { new_wire = module->addWire(NEW_ID, wire); module->swap_names(new_wire, wire); + wire->attributes.clear(); } if (flag_bits) { for (int i = 0; i < wire->width; i++) { + if (skip_bit_indices.count(i)) { + if (wire->port_output) + module->connect(SigSpec(new_wire, i), SigSpec(wire, i)); + else + module->connect(SigSpec(wire, i), SigSpec(new_wire, i)); + continue; + } + RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(celltype)); cell->setPort(RTLIL::escape_id(portname), RTLIL::SigSpec(wire, i)); if (!portname2.empty()) @@ -209,5 +360,5 @@ struct IopadmapPass : public Pass { } } } IopadmapPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc index 50d31ab5a..d5254c029 100644 --- a/passes/techmap/libparse.cc +++ b/passes/techmap/libparse.cc @@ -2,11 +2,11 @@ * 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 @@ -43,8 +43,6 @@ LibertyAst::~LibertyAst() LibertyAst *LibertyAst::find(std::string name) { - if (this == NULL) - return NULL; for (auto child : children) if (child->id == name) return child; @@ -107,14 +105,14 @@ int LibertyParser::lexer(std::string &str) } if (c == '"') { - str = c; + str = ""; while (1) { c = f.get(); if (c == '\n') line++; - str += c; if (c == '"') break; + str += c; } // fprintf(stderr, "LEX: string >>%s<<\n", str.c_str()); return 'v'; @@ -175,7 +173,7 @@ LibertyAst *LibertyParser::parse() if (tok == '}' || tok < 0) return NULL; - + if (tok != 'v') error(); diff --git a/passes/techmap/libparse.h b/passes/techmap/libparse.h index e947bd8cd..cf6325570 100644 --- a/passes/techmap/libparse.h +++ b/passes/techmap/libparse.h @@ -2,11 +2,11 @@ * 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 diff --git a/passes/techmap/lut2mux.cc b/passes/techmap/lut2mux.cc new file mode 100644 index 000000000..2bb0bd8b4 --- /dev/null +++ b/passes/techmap/lut2mux.cc @@ -0,0 +1,93 @@ +/* + * 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 + +int lut2mux(Cell *cell) +{ + SigSpec sig_a = cell->getPort("\\A"); + SigSpec sig_y = cell->getPort("\\Y"); + Const lut = cell->getParam("\\LUT"); + int count = 1; + + if (GetSize(sig_a) == 1) + { + cell->module->addMuxGate(NEW_ID, lut[0], lut[1], sig_a, sig_y); + } + else + { + SigSpec sig_a_hi = sig_a[GetSize(sig_a)-1]; + SigSpec sig_a_lo = sig_a.extract(0, GetSize(sig_a)-1); + SigSpec sig_y1 = cell->module->addWire(NEW_ID); + SigSpec sig_y2 = cell->module->addWire(NEW_ID); + + Const lut1 = lut.extract(0, GetSize(lut)/2); + Const lut2 = lut.extract(GetSize(lut)/2, GetSize(lut)/2); + + count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y1, lut1)); + count += lut2mux(cell->module->addLut(NEW_ID, sig_a_lo, sig_y2, lut2)); + + cell->module->addMuxGate(NEW_ID, sig_y1, sig_y2, sig_a_hi, sig_y); + } + + cell->module->remove(cell); + return count; +} + +struct Lut2muxPass : public Pass { + Lut2muxPass() : Pass("lut2mux", "convert $lut to $_MUX_") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" lut2mux [options] [selection]\n"); + log("\n"); + log("This pass converts $lut cells to $_MUX_ gates.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing LUT2MUX pass (convert $lut to $_MUX_).\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()) + for (auto cell : module->selected_cells()) { + if (cell->type == "$lut") { + IdString cell_name = cell->name; + int count = lut2mux(cell); + log("Converted %s.%s to %d MUX cells.\n", log_id(module), log_id(cell_name), count); + } + } + } +} Lut2muxPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc index ffbd6289d..32569d076 100644 --- a/passes/techmap/maccmap.cc +++ b/passes/techmap/maccmap.cc @@ -2,11 +2,11 @@ * 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 @@ -134,7 +134,7 @@ struct MaccmapWorker } return retval; #else - return std::max(n - 1, 0); + return max(n - 1, 0); #endif } @@ -371,15 +371,15 @@ struct MaccmapPass : public Pass { log("\n"); log(" maccmap [-unmap] [selection]\n"); log("\n"); - log("This pass maps $macc cells to yosys gate primitives. When the -unmap option is\n"); - log("used then the $macc cell is mapped to $and, $sub, etc. cells instead.\n"); + log("This pass maps $macc cells to yosys $fa and $alu cells. When the -unmap option\n"); + log("is used then the $macc cell is mapped to $add, $sub, etc. cells instead.\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { bool unmap_mode = false; - log_header("Executing MACCMAP pass (map $macc cells).\n"); + log_header(design, "Executing MACCMAP pass (map $macc cells).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -400,5 +400,5 @@ struct MaccmapPass : public Pass { } } } MaccmapPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc new file mode 100644 index 000000000..1dc649587 --- /dev/null +++ b/passes/techmap/muxcover.cc @@ -0,0 +1,632 @@ +/* + * 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 + +#define COST_MUX2 100 +#define COST_MUX4 220 +#define COST_MUX8 460 +#define COST_MUX16 940 + +struct MuxcoverWorker +{ + Module *module; + SigMap sigmap; + + struct newmux_t + { + int cost; + vector<SigBit> inputs, selects; + newmux_t() : cost(0) {} + }; + + struct tree_t + { + SigBit root; + dict<SigBit, Cell*> muxes; + dict<SigBit, newmux_t> newmuxes; + }; + + vector<tree_t> tree_list; + + 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; + bool use_mux8; + bool use_mux16; + bool nodecode; + + MuxcoverWorker(Module *module) : module(module), sigmap(module) + { + use_mux4 = false; + use_mux8 = false; + use_mux16 = false; + nodecode = false; + decode_mux_counter = 0; + } + + void treeify() + { + pool<SigBit> roots; + pool<SigBit> used_once; + dict<SigBit, Cell*> sig_to_mux; + + for (auto wire : module->wires()) { + if (!wire->port_output) + continue; + for (auto bit : sigmap(wire)) + roots.insert(bit); + } + + for (auto cell : module->cells()) { + for (auto conn : cell->connections()) { + if (!cell->input(conn.first)) + continue; + for (auto bit : sigmap(conn.second)) { + if (used_once.count(bit) || cell->type != "$_MUX_" || conn.first == "\\S") + roots.insert(bit); + used_once.insert(bit); + } + } + if (cell->type == "$_MUX_") + sig_to_mux[sigmap(cell->getPort("\\Y"))] = cell; + } + + log(" Treeifying %d MUXes:\n", GetSize(sig_to_mux)); + + roots.sort(); + for (auto rootsig : roots) + { + tree_t tree; + tree.root = rootsig; + + pool<SigBit> wavefront; + wavefront.insert(rootsig); + + while (!wavefront.empty()) { + SigBit bit = wavefront.pop(); + if (sig_to_mux.count(bit) && (bit == rootsig || !roots.count(bit))) { + Cell *c = sig_to_mux.at(bit); + tree.muxes[bit] = c; + wavefront.insert(sigmap(c->getPort("\\A"))); + wavefront.insert(sigmap(c->getPort("\\B"))); + } + } + + if (!tree.muxes.empty()) { + log(" Found tree with %d MUXes at root %s.\n", GetSize(tree.muxes), log_signal(tree.root)); + tree_list.push_back(tree); + } + } + + log(" Finished treeification: Found %d trees.\n", GetSize(tree_list)); + } + + bool follow_muxtree(SigBit &ret_bit, tree_t &tree, SigBit bit, const char *path) + { + if (*path) { + if (tree.muxes.count(bit) == 0) + return false; + char port_name[3] = {'\\', *path, 0}; + return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(port_name)), path+1); + } else { + ret_bit = bit; + return true; + } + } + + int prepare_decode_mux(SigBit &A, SigBit B, SigBit sel, SigBit bit) + { + if (A == B) + return 0; + + 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); + std::get<2>(entry) = false; + decode_mux_reverse_cache[std::get<0>(entry)] = key; + } + + auto &entry = decode_mux_cache[key]; + A = std::get<0>(entry); + std::get<1>(entry).insert(bit); + + if (std::get<2>(entry)) + return 0; + + return COST_MUX2 / GetSize(std::get<1>(entry)); + } + + void implement_decode_mux(SigBit ctrl_bit) + { + if (decode_mux_reverse_cache.count(ctrl_bit) == 0) + return; + + auto &key = decode_mux_reverse_cache.at(ctrl_bit); + auto &entry = decode_mux_cache[key]; + + if (std::get<2>(entry)) + return; + + implement_decode_mux(std::get<0>(key)); + implement_decode_mux(std::get<1>(key)); + + module->addMuxGate(NEW_ID, std::get<0>(key), std::get<1>(key), std::get<2>(key), ctrl_bit); + std::get<2>(entry) = true; + decode_mux_counter++; + } + + int find_best_cover(tree_t &tree, SigBit bit) + { + if (tree.newmuxes.count(bit)) { + return tree.newmuxes.at(bit).cost; + } + + SigBit A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P; + SigBit S1, S2, S3, S4, S5, S6, S7, S8; + SigBit T1, T2, T3, T4; + SigBit U1, U2; + SigBit V1; + + newmux_t best_mux; + bool ok = true; + + // 2-Input MUX + + ok = ok && follow_muxtree(A, tree, bit, "A"); + ok = ok && follow_muxtree(B, tree, bit, "B"); + + ok = ok && follow_muxtree(S1, tree, bit, "S"); + + if (ok) + { + newmux_t mux; + + mux.inputs.push_back(A); + mux.inputs.push_back(B); + mux.selects.push_back(S1); + + mux.cost += COST_MUX2; + mux.cost += find_best_cover(tree, A); + mux.cost += find_best_cover(tree, B); + + best_mux = mux; + } + + // 4-Input MUX + + if (use_mux4) + { + ok = ok && follow_muxtree(A, tree, bit, "AA"); + ok = ok && follow_muxtree(B, tree, bit, "AB"); + ok = ok && follow_muxtree(C, tree, bit, "BA"); + ok = ok && follow_muxtree(D, tree, bit, "BB"); + + ok = ok && follow_muxtree(S1, tree, bit, "AS"); + ok = ok && follow_muxtree(S2, tree, bit, "BS"); + + if (nodecode) + ok = ok && S1 == S2; + + ok = ok && follow_muxtree(T1, tree, bit, "S"); + + if (ok) + { + newmux_t mux; + + mux.inputs.push_back(A); + mux.inputs.push_back(B); + mux.inputs.push_back(C); + mux.inputs.push_back(D); + + mux.cost += prepare_decode_mux(S1, S2, T1, bit); + + mux.selects.push_back(S1); + mux.selects.push_back(T1); + + mux.cost += COST_MUX4; + mux.cost += find_best_cover(tree, A); + mux.cost += find_best_cover(tree, B); + mux.cost += find_best_cover(tree, C); + mux.cost += find_best_cover(tree, D); + + if (best_mux.cost > mux.cost) + best_mux = mux; + } + } + + // 8-Input MUX + + if (use_mux8) + { + ok = ok && follow_muxtree(A, tree, bit, "AAA"); + ok = ok && follow_muxtree(B, tree, bit, "AAB"); + ok = ok && follow_muxtree(C, tree, bit, "ABA"); + ok = ok && follow_muxtree(D, tree, bit, "ABB"); + ok = ok && follow_muxtree(E, tree, bit, "BAA"); + ok = ok && follow_muxtree(F, tree, bit, "BAB"); + ok = ok && follow_muxtree(G, tree, bit, "BBA"); + ok = ok && follow_muxtree(H, tree, bit, "BBB"); + + ok = ok && follow_muxtree(S1, tree, bit, "AAS"); + ok = ok && follow_muxtree(S2, tree, bit, "ABS"); + ok = ok && follow_muxtree(S3, tree, bit, "BAS"); + ok = ok && follow_muxtree(S4, tree, bit, "BBS"); + + if (nodecode) + ok = ok && S1 == S2 && S2 == S3 && S3 == S4; + + ok = ok && follow_muxtree(T1, tree, bit, "AS"); + ok = ok && follow_muxtree(T2, tree, bit, "BS"); + + if (nodecode) + ok = ok && T1 == T2; + + ok = ok && follow_muxtree(U1, tree, bit, "S"); + + if (ok) + { + newmux_t mux; + + mux.inputs.push_back(A); + mux.inputs.push_back(B); + mux.inputs.push_back(C); + mux.inputs.push_back(D); + mux.inputs.push_back(E); + mux.inputs.push_back(F); + mux.inputs.push_back(G); + mux.inputs.push_back(H); + + mux.cost += prepare_decode_mux(S1, S2, T1, bit); + mux.cost += prepare_decode_mux(S3, S4, T2, bit); + mux.cost += prepare_decode_mux(S1, S3, U1, bit); + + mux.cost += prepare_decode_mux(T1, T2, U1, bit); + + mux.selects.push_back(S1); + mux.selects.push_back(T1); + mux.selects.push_back(U1); + + mux.cost += COST_MUX8; + mux.cost += find_best_cover(tree, A); + mux.cost += find_best_cover(tree, B); + mux.cost += find_best_cover(tree, C); + mux.cost += find_best_cover(tree, D); + mux.cost += find_best_cover(tree, E); + mux.cost += find_best_cover(tree, F); + mux.cost += find_best_cover(tree, G); + mux.cost += find_best_cover(tree, H); + + if (best_mux.cost > mux.cost) + best_mux = mux; + } + } + + // 16-Input MUX + + if (use_mux16) + { + ok = ok && follow_muxtree(A, tree, bit, "AAAA"); + ok = ok && follow_muxtree(B, tree, bit, "AAAB"); + ok = ok && follow_muxtree(C, tree, bit, "AABA"); + ok = ok && follow_muxtree(D, tree, bit, "AABB"); + ok = ok && follow_muxtree(E, tree, bit, "ABAA"); + ok = ok && follow_muxtree(F, tree, bit, "ABAB"); + ok = ok && follow_muxtree(G, tree, bit, "ABBA"); + ok = ok && follow_muxtree(H, tree, bit, "ABBB"); + ok = ok && follow_muxtree(I, tree, bit, "BAAA"); + ok = ok && follow_muxtree(J, tree, bit, "BAAB"); + ok = ok && follow_muxtree(K, tree, bit, "BABA"); + ok = ok && follow_muxtree(L, tree, bit, "BABB"); + ok = ok && follow_muxtree(M, tree, bit, "BBAA"); + ok = ok && follow_muxtree(N, tree, bit, "BBAB"); + ok = ok && follow_muxtree(O, tree, bit, "BBBA"); + ok = ok && follow_muxtree(P, tree, bit, "BBBB"); + + ok = ok && follow_muxtree(S1, tree, bit, "AAAS"); + ok = ok && follow_muxtree(S2, tree, bit, "AABS"); + ok = ok && follow_muxtree(S3, tree, bit, "ABAS"); + ok = ok && follow_muxtree(S4, tree, bit, "ABBS"); + ok = ok && follow_muxtree(S5, tree, bit, "BAAS"); + ok = ok && follow_muxtree(S6, tree, bit, "BABS"); + ok = ok && follow_muxtree(S7, tree, bit, "BBAS"); + ok = ok && follow_muxtree(S8, tree, bit, "BBBS"); + + if (nodecode) + ok = ok && S1 == S2 && S2 == S3 && S3 == S4 && S4 == S5 && S5 == S6 && S6 == S7 && S7 == S8; + + ok = ok && follow_muxtree(T1, tree, bit, "AAS"); + ok = ok && follow_muxtree(T2, tree, bit, "ABS"); + ok = ok && follow_muxtree(T3, tree, bit, "BAS"); + ok = ok && follow_muxtree(T4, tree, bit, "BBS"); + + if (nodecode) + ok = ok && T1 == T2 && T2 == T3 && T3 == T4; + + ok = ok && follow_muxtree(U1, tree, bit, "AS"); + ok = ok && follow_muxtree(U2, tree, bit, "BS"); + + if (nodecode) + ok = ok && U1 == U2; + + ok = ok && follow_muxtree(V1, tree, bit, "S"); + + if (ok) + { + newmux_t mux; + + mux.inputs.push_back(A); + mux.inputs.push_back(B); + mux.inputs.push_back(C); + mux.inputs.push_back(D); + mux.inputs.push_back(E); + mux.inputs.push_back(F); + mux.inputs.push_back(G); + mux.inputs.push_back(H); + mux.inputs.push_back(I); + mux.inputs.push_back(J); + mux.inputs.push_back(K); + mux.inputs.push_back(L); + mux.inputs.push_back(M); + mux.inputs.push_back(N); + mux.inputs.push_back(O); + mux.inputs.push_back(P); + + mux.cost += prepare_decode_mux(S1, S2, T1, bit); + mux.cost += prepare_decode_mux(S3, S4, T2, bit); + mux.cost += prepare_decode_mux(S5, S6, T3, bit); + mux.cost += prepare_decode_mux(S7, S8, T4, bit); + mux.cost += prepare_decode_mux(S1, S3, U1, bit); + mux.cost += prepare_decode_mux(S5, S7, U2, bit); + mux.cost += prepare_decode_mux(S1, S5, V1, bit); + + mux.cost += prepare_decode_mux(T1, T2, U1, bit); + mux.cost += prepare_decode_mux(T3, T4, U2, bit); + mux.cost += prepare_decode_mux(T1, T3, V1, bit); + + mux.cost += prepare_decode_mux(U1, U2, V1, bit); + + mux.selects.push_back(S1); + mux.selects.push_back(T1); + mux.selects.push_back(U1); + mux.selects.push_back(V1); + + mux.cost += COST_MUX16; + mux.cost += find_best_cover(tree, A); + mux.cost += find_best_cover(tree, B); + mux.cost += find_best_cover(tree, C); + mux.cost += find_best_cover(tree, D); + mux.cost += find_best_cover(tree, E); + mux.cost += find_best_cover(tree, F); + mux.cost += find_best_cover(tree, G); + mux.cost += find_best_cover(tree, H); + mux.cost += find_best_cover(tree, I); + mux.cost += find_best_cover(tree, J); + mux.cost += find_best_cover(tree, K); + mux.cost += find_best_cover(tree, L); + mux.cost += find_best_cover(tree, M); + mux.cost += find_best_cover(tree, N); + mux.cost += find_best_cover(tree, O); + mux.cost += find_best_cover(tree, P); + + if (best_mux.cost > mux.cost) + best_mux = mux; + } + } + + tree.newmuxes[bit] = best_mux; + return best_mux.cost; + } + + void implement_best_cover(tree_t &tree, SigBit bit, int count_muxes_by_type[4]) + { + newmux_t mux = tree.newmuxes.at(bit); + + for (auto inbit : mux.inputs) + implement_best_cover(tree, inbit, count_muxes_by_type); + + for (auto selbit : mux.selects) + implement_decode_mux(selbit); + + if (GetSize(mux.inputs) == 0) + return; + + if (GetSize(mux.inputs) == 2) { + count_muxes_by_type[0]++; + Cell *cell = module->addCell(NEW_ID, "$_MUX_"); + cell->setPort("\\A", mux.inputs[0]); + cell->setPort("\\B", mux.inputs[1]); + cell->setPort("\\S", mux.selects[0]); + cell->setPort("\\Y", bit); + return; + } + + if (GetSize(mux.inputs) == 4) { + count_muxes_by_type[1]++; + Cell *cell = module->addCell(NEW_ID, "$_MUX4_"); + cell->setPort("\\A", mux.inputs[0]); + cell->setPort("\\B", mux.inputs[1]); + cell->setPort("\\C", mux.inputs[2]); + cell->setPort("\\D", mux.inputs[3]); + cell->setPort("\\S", mux.selects[0]); + cell->setPort("\\T", mux.selects[1]); + cell->setPort("\\Y", bit); + return; + } + + if (GetSize(mux.inputs) == 8) { + count_muxes_by_type[2]++; + Cell *cell = module->addCell(NEW_ID, "$_MUX8_"); + cell->setPort("\\A", mux.inputs[0]); + cell->setPort("\\B", mux.inputs[1]); + cell->setPort("\\C", mux.inputs[2]); + cell->setPort("\\D", mux.inputs[3]); + cell->setPort("\\E", mux.inputs[4]); + cell->setPort("\\F", mux.inputs[5]); + cell->setPort("\\G", mux.inputs[6]); + cell->setPort("\\H", mux.inputs[7]); + cell->setPort("\\S", mux.selects[0]); + cell->setPort("\\T", mux.selects[1]); + cell->setPort("\\U", mux.selects[2]); + cell->setPort("\\Y", bit); + return; + } + + if (GetSize(mux.inputs) == 16) { + count_muxes_by_type[3]++; + Cell *cell = module->addCell(NEW_ID, "$_MUX16_"); + cell->setPort("\\A", mux.inputs[0]); + cell->setPort("\\B", mux.inputs[1]); + cell->setPort("\\C", mux.inputs[2]); + cell->setPort("\\D", mux.inputs[3]); + cell->setPort("\\E", mux.inputs[4]); + cell->setPort("\\F", mux.inputs[5]); + cell->setPort("\\G", mux.inputs[6]); + cell->setPort("\\H", mux.inputs[7]); + cell->setPort("\\I", mux.inputs[8]); + cell->setPort("\\J", mux.inputs[9]); + cell->setPort("\\K", mux.inputs[10]); + cell->setPort("\\L", mux.inputs[11]); + cell->setPort("\\M", mux.inputs[12]); + cell->setPort("\\N", mux.inputs[13]); + cell->setPort("\\O", mux.inputs[14]); + cell->setPort("\\P", mux.inputs[15]); + cell->setPort("\\S", mux.selects[0]); + cell->setPort("\\T", mux.selects[1]); + cell->setPort("\\U", mux.selects[2]); + cell->setPort("\\V", mux.selects[3]); + cell->setPort("\\Y", bit); + return; + } + + log_abort(); + } + + void treecover(tree_t &tree) + { + int count_muxes_by_type[4] = {0, 0, 0, 0}; + find_best_cover(tree, tree.root); + implement_best_cover(tree, tree.root, count_muxes_by_type); + log(" Replaced tree at %s: %d MUX2, %d MUX4, %d MUX8, %d MUX16\n", log_signal(tree.root), + count_muxes_by_type[0], count_muxes_by_type[1], count_muxes_by_type[2], count_muxes_by_type[3]); + for (auto &it : tree.muxes) + module->remove(it.second); + } + + void run() + { + log("Covering MUX trees in module %s..\n", log_id(module)); + + treeify(); + + log(" Covering trees:\n"); + + // pre-fill cache of decoder muxes + if (!nodecode) + for (auto &tree : tree_list) { + find_best_cover(tree, tree.root); + tree.newmuxes.clear(); + } + + for (auto &tree : tree_list) + treecover(tree); + + if (!nodecode) + log(" Added a total of %d decoder MUXes.\n", decode_mux_counter); + } +}; + +struct MuxcoverPass : public Pass { + MuxcoverPass() : Pass("muxcover", "cover trees of MUX cells with wider MUXes") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" muxcover [options] [selection]\n"); + log("\n"); + log("Cover trees of $_MUX_ cells with $_MUX{4,8,16}_ cells\n"); + log("\n"); + log(" -mux4, -mux8, -mux16\n"); + log(" Use the specified types of MUXes. If none of those options are used,\n"); + log(" the effect is the same as if all of them where used.\n"); + log("\n"); + log(" -nodecode\n"); + log(" Do not insert decoder logic. This reduces the number of possible\n"); + log(" substitutions, but guarantees that the resulting circuit is not\n"); + log(" less efficient than the original circuit.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing MUXCOVER pass (mapping to wider MUXes).\n"); + + bool use_mux4 = false; + bool use_mux8 = false; + bool use_mux16 = false; + bool nodecode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-mux4") { + use_mux4 = true; + continue; + } + if (args[argidx] == "-mux8") { + use_mux8 = true; + continue; + } + if (args[argidx] == "-mux16") { + use_mux16 = true; + continue; + } + if (args[argidx] == "-nodecode") { + nodecode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!use_mux4 && !use_mux8 && !use_mux16) { + use_mux4 = true; + use_mux8 = true; + use_mux16 = true; + } + + for (auto module : design->selected_modules()) + { + MuxcoverWorker worker(module); + worker.use_mux4 = use_mux4; + worker.use_mux8 = use_mux8; + worker.use_mux16 = use_mux16; + worker.nodecode = nodecode; + worker.run(); + } + } +} MuxcoverPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/nlutmap.cc b/passes/techmap/nlutmap.cc new file mode 100644 index 000000000..6fcdf82bd --- /dev/null +++ b/passes/techmap/nlutmap.cc @@ -0,0 +1,187 @@ +/* + * 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 NlutmapConfig +{ + vector<int> luts; + bool assert_mode = false; +}; + +struct NlutmapWorker +{ + const NlutmapConfig &config; + pool<Cell*> mapped_cells; + Module *module; + + NlutmapWorker(const NlutmapConfig &config, Module *module) : + config(config), module(module) + { + } + + RTLIL::Selection get_selection() + { + RTLIL::Selection sel(false); + for (auto cell : module->cells()) + if (!mapped_cells.count(cell)) + sel.select(module, cell); + return sel; + } + + void run_abc(int lut_size) + { + Pass::call_on_selection(module->design, get_selection(), "lut2mux"); + + if (lut_size > 0) + Pass::call_on_selection(module->design, get_selection(), stringf("abc -lut 1:%d", lut_size)); + else + Pass::call_on_selection(module->design, get_selection(), "abc"); + + Pass::call_on_module(module->design, module, "opt_clean"); + } + + void run() + { + vector<int> available_luts = config.luts; + + while (GetSize(available_luts) > 1) + { + int n_luts = available_luts.back(); + int lut_size = GetSize(available_luts); + available_luts.pop_back(); + + if (n_luts == 0) + continue; + + run_abc(lut_size); + + SigMap sigmap(module); + dict<Cell*, int> candidate_ratings; + dict<SigBit, int> bit_lut_count; + + for (auto cell : module->cells()) + { + if (cell->type != "$lut" || mapped_cells.count(cell)) + continue; + + if (GetSize(cell->getPort("\\A")) == lut_size || lut_size == 2) + candidate_ratings[cell] = 0; + + for (auto &conn : cell->connections()) + for (auto bit : sigmap(conn.second)) + bit_lut_count[bit]++; + } + + for (auto &cand : candidate_ratings) + { + for (auto &conn : cand.first->connections()) + for (auto bit : sigmap(conn.second)) + cand.second -= bit_lut_count[bit]; + } + + vector<pair<int, IdString>> rated_candidates; + + for (auto &cand : candidate_ratings) + rated_candidates.push_back(pair<int, IdString>(cand.second, cand.first->name)); + + std::sort(rated_candidates.begin(), rated_candidates.end()); + + while (n_luts > 0 && !rated_candidates.empty()) { + mapped_cells.insert(module->cell(rated_candidates.back().second)); + rated_candidates.pop_back(); + n_luts--; + } + + if (!available_luts.empty()) + available_luts.back() += n_luts; + } + + if (config.assert_mode) { + for (auto cell : module->cells()) + if (cell->type == "$lut" && !mapped_cells.count(cell)) + log_error("Insufficient number of LUTs to map all logic cells!\n"); + } + + run_abc(0); + } +}; + +struct NlutmapPass : public Pass { + NlutmapPass() : Pass("nlutmap", "map to LUTs of different sizes") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" nlutmap [options] [selection]\n"); + log("\n"); + log("This pass uses successive calls to 'abc' to map to an architecture. That\n"); + log("provides a small number of differently sized LUTs.\n"); + log("\n"); + log(" -luts N_1,N_2,N_3,...\n"); + log(" The number of LUTs with 1, 2, 3, ... inputs that are\n"); + log(" available in the target architecture.\n"); + log("\n"); + log(" -assert\n"); + log(" Create an error if not all logic can be mapped\n"); + log("\n"); + log("Excess logic that does not fit into the specified LUTs is mapped back\n"); + log("to generic logic gates ($_AND_, etc.).\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + NlutmapConfig config; + + log_header(design, "Executing NLUTMAP pass (mapping to constant drivers).\n"); + log_push(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-luts" && argidx+1 < args.size()) { + vector<string> tokens = split_tokens(args[++argidx], ","); + config.luts.clear(); + for (auto &token : tokens) + config.luts.push_back(atoi(token.c_str())); + continue; + } + if (args[argidx] == "-assert") { + config.assert_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_whole_modules_warn()) + { + NlutmapWorker worker(config, module); + worker.run(); + } + + log_pop(); + } +} NlutmapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/pmuxtree.cc b/passes/techmap/pmuxtree.cc new file mode 100644 index 000000000..c626dbcc5 --- /dev/null +++ b/passes/techmap/pmuxtree.cc @@ -0,0 +1,112 @@ +/* + * 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 + +static SigSpec or_generator(Module *module, const SigSpec &sig) +{ + switch (GetSize(sig)) + { + case 0: + return State::S0; + case 1: + return sig; + case 2: + return module->Or(NEW_ID, sig[0], sig[1]); + default: + return module->ReduceOr(NEW_ID, sig); + } +} + +static SigSpec recursive_mux_generator(Module *module, const SigSpec &sig_data, const SigSpec &sig_sel, SigSpec &sig_or) +{ + if (GetSize(sig_sel) == 1) { + sig_or.append(sig_sel); + return sig_data; + } + + int left_size = GetSize(sig_sel) / 2; + int right_size = GetSize(sig_sel) - left_size; + int stride = GetSize(sig_data) / GetSize(sig_sel); + + SigSpec left_data = sig_data.extract(0, stride*left_size); + SigSpec right_data = sig_data.extract(stride*left_size, stride*right_size); + + SigSpec left_sel = sig_sel.extract(0, left_size); + SigSpec right_sel = sig_sel.extract(left_size, right_size); + + SigSpec left_or, left_result, right_result; + + left_result = recursive_mux_generator(module, left_data, left_sel, left_or); + right_result = recursive_mux_generator(module, right_data, right_sel, sig_or); + left_or = or_generator(module, left_or); + sig_or.append(left_or); + + return module->Mux(NEW_ID, right_result, left_result, left_or); +} + +struct PmuxtreePass : public Pass { + PmuxtreePass() : Pass("pmuxtree", "transform $pmux cells to trees of $mux cells") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" pmuxtree [options] [selection]\n"); + log("\n"); + log("This pass transforms $pmux cells to a trees of $mux cells.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing PMUXTREE pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + for (auto cell : module->selected_cells()) + { + if (cell->type != "$pmux") + continue; + + SigSpec sig_data = cell->getPort("\\B"); + SigSpec sig_sel = cell->getPort("\\S"); + + if (!cell->getPort("\\A").is_fully_undef()) { + sig_data.append(cell->getPort("\\A")); + SigSpec sig_sel_or = module->ReduceOr(NEW_ID, sig_sel); + sig_sel.append(module->Not(NEW_ID, sig_sel_or)); + } + + SigSpec result, result_or; + result = recursive_mux_generator(module, sig_data, sig_sel, result_or); + module->connect(cell->getPort("\\Y"), result); + module->remove(cell); + } + } +} PmuxtreePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc new file mode 100644 index 000000000..6936b499e --- /dev/null +++ b/passes/techmap/shregmap.cc @@ -0,0 +1,584 @@ +/* + * 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 ShregmapTech +{ + virtual ~ShregmapTech() { } + virtual bool analyze(vector<int> &taps) = 0; + virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0; +}; + +struct ShregmapOptions +{ + int minlen, maxlen; + int keep_before, keep_after; + bool zinit, init, params, ffe; + dict<IdString, pair<IdString, IdString>> ffcells; + ShregmapTech *tech; + + ShregmapOptions() + { + minlen = 2; + maxlen = 0; + keep_before = 0; + keep_after = 0; + zinit = false; + init = false; + params = false; + ffe = false; + tech = nullptr; + } +}; + +struct ShregmapTechGreenpak4 : ShregmapTech +{ + bool analyze(vector<int> &taps) + { + if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) { + taps.clear(); + return true; + } + + if (GetSize(taps) > 2) + return false; + + if (taps.back() > 16) return false; + + return true; + } + + bool fixup(Cell *cell, dict<int, SigBit> &taps) + { + auto D = cell->getPort("\\D"); + auto C = cell->getPort("\\C"); + + auto newcell = cell->module->addCell(NEW_ID, "\\GP_SHREG"); + newcell->setPort("\\nRST", State::S1); + newcell->setPort("\\CLK", C); + newcell->setPort("\\IN", D); + + int i = 0; + for (auto tap : taps) { + newcell->setPort(i ? "\\OUTB" : "\\OUTA", tap.second); + newcell->setParam(i ? "\\OUTB_TAP" : "\\OUTA_TAP", tap.first + 1); + i++; + } + + cell->setParam("\\OUTA_INVERT", 0); + return false; + } +}; + +struct ShregmapWorker +{ + Module *module; + SigMap sigmap; + + const ShregmapOptions &opts; + int dff_count, shreg_count; + + pool<Cell*> remove_cells; + pool<SigBit> remove_init; + + dict<SigBit, bool> sigbit_init; + dict<SigBit, Cell*> sigbit_chain_next; + dict<SigBit, Cell*> sigbit_chain_prev; + pool<SigBit> sigbit_with_non_chain_users; + pool<Cell*> chain_start_cells; + + void make_sigbit_chain_next_prev() + { + for (auto wire : module->wires()) + { + if (wire->port_output || wire->get_bool_attribute("\\keep")) { + for (auto bit : sigmap(wire)) + sigbit_with_non_chain_users.insert(bit); + } + + if (wire->attributes.count("\\init")) { + SigSpec initsig = sigmap(wire); + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) + if (initval[i] == State::S0 && !opts.zinit) + sigbit_init[initsig[i]] = false; + else if (initval[i] == State::S1) + sigbit_init[initsig[i]] = true; + } + } + + for (auto cell : module->cells()) + { + if (opts.ffcells.count(cell->type) && !cell->get_bool_attribute("\\keep")) + { + IdString d_port = opts.ffcells.at(cell->type).first; + IdString q_port = opts.ffcells.at(cell->type).second; + + SigBit d_bit = sigmap(cell->getPort(d_port).as_bit()); + SigBit q_bit = sigmap(cell->getPort(q_port).as_bit()); + + if (opts.init || sigbit_init.count(q_bit) == 0) + { + if (sigbit_chain_next.count(d_bit)) { + sigbit_with_non_chain_users.insert(d_bit); + } else + sigbit_chain_next[d_bit] = cell; + + sigbit_chain_prev[q_bit] = cell; + continue; + } + } + + for (auto conn : cell->connections()) + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + sigbit_with_non_chain_users.insert(bit); + } + } + + void find_chain_start_cells() + { + for (auto it : sigbit_chain_next) + { + if (opts.tech == nullptr && sigbit_with_non_chain_users.count(it.first)) + goto start_cell; + + if (sigbit_chain_prev.count(it.first) != 0) + { + Cell *c1 = sigbit_chain_prev.at(it.first); + Cell *c2 = it.second; + + if (c1->type != c2->type) + goto start_cell; + + if (c1->parameters != c2->parameters) + goto start_cell; + + IdString d_port = opts.ffcells.at(c1->type).first; + IdString q_port = opts.ffcells.at(c1->type).second; + + auto c1_conn = c1->connections(); + auto c2_conn = c1->connections(); + + c1_conn.erase(d_port); + c1_conn.erase(q_port); + + c2_conn.erase(d_port); + c2_conn.erase(q_port); + + if (c1_conn != c2_conn) + goto start_cell; + + continue; + } + + start_cell: + chain_start_cells.insert(it.second); + } + } + + vector<Cell*> create_chain(Cell *start_cell) + { + vector<Cell*> chain; + + Cell *c = start_cell; + while (c != nullptr) + { + chain.push_back(c); + + IdString q_port = opts.ffcells.at(c->type).second; + SigBit q_bit = sigmap(c->getPort(q_port).as_bit()); + + if (sigbit_chain_next.count(q_bit) == 0) + break; + + c = sigbit_chain_next.at(q_bit); + if (chain_start_cells.count(c) != 0) + break; + } + + return chain; + } + + void process_chain(vector<Cell*> &chain) + { + if (GetSize(chain) < opts.keep_before + opts.minlen + opts.keep_after) + return; + + int cursor = opts.keep_before; + while (cursor < GetSize(chain) - opts.keep_after) + { + int depth = GetSize(chain) - opts.keep_after - cursor; + + if (opts.maxlen > 0) + depth = std::min(opts.maxlen, depth); + + Cell *first_cell = chain[cursor]; + IdString q_port = opts.ffcells.at(first_cell->type).second; + dict<int, SigBit> taps_dict; + + if (opts.tech) + { + vector<SigBit> qbits; + vector<int> taps; + + for (int i = 0; i < depth; i++) + { + Cell *cell = chain[cursor+i]; + auto qbit = sigmap(cell->getPort(q_port)); + qbits.push_back(qbit); + + if (sigbit_with_non_chain_users.count(qbit)) + taps.push_back(i); + } + + while (depth > 0) + { + if (taps.empty() || taps.back() < depth-1) + taps.push_back(depth-1); + + if (opts.tech->analyze(taps)) + break; + + taps.pop_back(); + depth--; + } + + depth = 0; + for (auto tap : taps) { + taps_dict[tap] = qbits.at(tap); + log_assert(depth < tap+1); + depth = tap+1; + } + } + + if (depth < 2) { + cursor++; + continue; + } + + Cell *last_cell = chain[cursor+depth-1]; + + log("Converting %s.%s ... %s.%s to a shift register with depth %d.\n", + log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), depth); + + dff_count += depth; + shreg_count += 1; + + string shreg_cell_type_str = "$__SHREG"; + if (opts.params) { + shreg_cell_type_str += "_"; + } else { + if (first_cell->type[1] != '_') + shreg_cell_type_str += "_"; + shreg_cell_type_str += first_cell->type.substr(1); + } + + if (opts.init) { + vector<State> initval; + for (int i = depth-1; i >= 0; i--) { + SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit()); + if (sigbit_init.count(bit) == 0) + initval.push_back(State::Sx); + else if (sigbit_init.at(bit)) + initval.push_back(State::S1); + else + initval.push_back(State::S0); + remove_init.insert(bit); + } + first_cell->setParam("\\INIT", initval); + } + + if (opts.zinit) + for (int i = depth-1; i >= 0; i--) { + SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit()); + remove_init.insert(bit); + } + + if (opts.params) + { + int param_clkpol = -1; + int param_enpol = 2; + + if (first_cell->type == "$_DFF_N_") param_clkpol = 0; + if (first_cell->type == "$_DFF_P_") param_clkpol = 1; + + if (first_cell->type == "$_DFFE_NN_") param_clkpol = 0, param_enpol = 0; + if (first_cell->type == "$_DFFE_NP_") param_clkpol = 0, param_enpol = 1; + if (first_cell->type == "$_DFFE_PN_") param_clkpol = 1, param_enpol = 0; + if (first_cell->type == "$_DFFE_PP_") param_clkpol = 1, param_enpol = 1; + + log_assert(param_clkpol >= 0); + first_cell->setParam("\\CLKPOL", param_clkpol); + if (opts.ffe) first_cell->setParam("\\ENPOL", param_enpol); + } + + first_cell->type = shreg_cell_type_str; + first_cell->setPort(q_port, last_cell->getPort(q_port)); + first_cell->setParam("\\DEPTH", depth); + + if (opts.tech != nullptr && !opts.tech->fixup(first_cell, taps_dict)) + remove_cells.insert(first_cell); + + for (int i = 1; i < depth; i++) + remove_cells.insert(chain[cursor+i]); + cursor += depth; + } + } + + void cleanup() + { + for (auto cell : remove_cells) + module->remove(cell); + + for (auto wire : module->wires()) + { + if (wire->attributes.count("\\init") == 0) + continue; + + SigSpec initsig = sigmap(wire); + Const &initval = wire->attributes.at("\\init"); + + for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) + if (remove_init.count(initsig[i])) + initval[i] = State::Sx; + + if (SigSpec(initval).is_fully_undef()) + wire->attributes.erase("\\init"); + } + + remove_cells.clear(); + sigbit_chain_next.clear(); + sigbit_chain_prev.clear(); + chain_start_cells.clear(); + } + + ShregmapWorker(Module *module, const ShregmapOptions &opts) : + module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0) + { + make_sigbit_chain_next_prev(); + find_chain_start_cells(); + + for (auto c : chain_start_cells) { + vector<Cell*> chain = create_chain(c); + process_chain(chain); + } + + cleanup(); + } +}; + +struct ShregmapPass : public Pass { + ShregmapPass() : Pass("shregmap", "map shift registers") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" shregmap [options] [selection]\n"); + log("\n"); + log("This pass converts chains of $_DFF_[NP]_ gates to target specific shift register\n"); + log("primitives. The generated shift register will be of type $__SHREG_DFF_[NP]_ and\n"); + log("will use the same interface as the original $_DFF_*_ cells. The cell parameter\n"); + log("'DEPTH' will contain the depth of the shift register. Use a target-specific\n"); + log("'techmap' map file to convert those cells to the actual target cells.\n"); + log("\n"); + log(" -minlen N\n"); + log(" minimum length of shift register (default = 2)\n"); + log(" (this is the length after -keep_before and -keep_after)\n"); + log("\n"); + log(" -maxlen N\n"); + log(" maximum length of shift register (default = no limit)\n"); + log(" larger chains will be mapped to multiple shift register instances\n"); + log("\n"); + log(" -keep_before N\n"); + log(" number of DFFs to keep before the shift register (default = 0)\n"); + log("\n"); + log(" -keep_after N\n"); + log(" number of DFFs to keep after the shift register (default = 0)\n"); + log("\n"); + log(" -clkpol pos|neg|any\n"); + log(" limit match to only positive or negative edge clocks. (default = any)\n"); + log("\n"); + log(" -enpol pos|neg|none|any_or_none|any\n"); + log(" limit match to FFs with the specified enable polarity. (default = none)\n"); + log("\n"); + log(" -match <cell_type>[:<d_port_name>:<q_port_name>]\n"); + log(" match the specified cells instead of $_DFF_N_ and $_DFF_P_. If\n"); + log(" ':<d_port_name>:<q_port_name>' is omitted then 'D' and 'Q' is used\n"); + log(" by default. E.g. the option '-clkpol pos' is just an alias for\n"); + log(" '-match $_DFF_P_', which is an alias for '-match $_DFF_P_:D:Q'.\n"); + log("\n"); + log(" -params\n"); + log(" instead of encoding the clock and enable polarity in the cell name by\n"); + log(" deriving from the original cell name, simply name all generated cells\n"); + log(" $__SHREG_ and use CLKPOL and ENPOL parameters. An ENPOL value of 2 is\n"); + log(" used to denote cells without enable input. The ENPOL parameter is\n"); + log(" omitted when '-enpol none' (or no -enpol option) is passed.\n"); + log("\n"); + log(" -zinit\n"); + log(" assume the shift register is automatically zero-initialized, so it\n"); + log(" becomes legal to merge zero initialized FFs into the shift register.\n"); + log("\n"); + log(" -init\n"); + log(" map initialized registers to the shift reg, add an INIT parameter to\n"); + log(" generated cells with the initialization value. (first bit to shift out\n"); + log(" in LSB position)\n"); + log("\n"); + log(" -tech greenpak4\n"); + log(" map to greenpak4 shift registers.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + ShregmapOptions opts; + string clkpol, enpol; + + log_header(design, "Executing SHREGMAP pass (map shift registers).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-clkpol" && argidx+1 < args.size()) { + clkpol = args[++argidx]; + continue; + } + if (args[argidx] == "-enpol" && argidx+1 < args.size()) { + enpol = args[++argidx]; + continue; + } + if (args[argidx] == "-match" && argidx+1 < args.size()) { + vector<string> match_args = split_tokens(args[++argidx], ":"); + if (GetSize(match_args) < 2) + match_args.push_back("D"); + if (GetSize(match_args) < 3) + match_args.push_back("Q"); + IdString id_cell_type(RTLIL::escape_id(match_args[0])); + IdString id_d_port_name(RTLIL::escape_id(match_args[1])); + IdString id_q_port_name(RTLIL::escape_id(match_args[2])); + opts.ffcells[id_cell_type] = make_pair(id_d_port_name, id_q_port_name); + continue; + } + if (args[argidx] == "-minlen" && argidx+1 < args.size()) { + opts.minlen = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-maxlen" && argidx+1 < args.size()) { + opts.maxlen = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-keep_before" && argidx+1 < args.size()) { + opts.keep_before = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-keep_after" && argidx+1 < args.size()) { + opts.keep_after = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-tech" && argidx+1 < args.size() && opts.tech == nullptr) { + string tech = args[++argidx]; + if (tech == "greenpak4") { + clkpol = "pos"; + opts.zinit = true; + opts.tech = new ShregmapTechGreenpak4; + } else { + argidx--; + break; + } + continue; + } + if (args[argidx] == "-zinit") { + opts.zinit = true; + continue; + } + if (args[argidx] == "-init") { + opts.init = true; + continue; + } + if (args[argidx] == "-params") { + opts.params = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (opts.zinit && opts.init) + log_cmd_error("Options -zinit and -init are exclusive!\n"); + + if (opts.ffcells.empty()) + { + bool clk_pos = clkpol == "" || clkpol == "pos" || clkpol == "any"; + bool clk_neg = clkpol == "" || clkpol == "neg" || clkpol == "any"; + + bool en_none = enpol == "" || enpol == "none" || enpol == "any_or_none"; + bool en_pos = enpol == "pos" || enpol == "any" || enpol == "any_or_none"; + bool en_neg = enpol == "neg" || enpol == "any" || enpol == "any_or_none"; + + if (clk_pos && en_none) + opts.ffcells["$_DFF_P_"] = make_pair(IdString("\\D"), IdString("\\Q")); + if (clk_neg && en_none) + opts.ffcells["$_DFF_N_"] = make_pair(IdString("\\D"), IdString("\\Q")); + + if (clk_pos && en_pos) + opts.ffcells["$_DFFE_PP_"] = make_pair(IdString("\\D"), IdString("\\Q")); + if (clk_pos && en_neg) + opts.ffcells["$_DFFE_PN_"] = make_pair(IdString("\\D"), IdString("\\Q")); + + if (clk_neg && en_pos) + opts.ffcells["$_DFFE_NP_"] = make_pair(IdString("\\D"), IdString("\\Q")); + if (clk_neg && en_neg) + opts.ffcells["$_DFFE_NN_"] = make_pair(IdString("\\D"), IdString("\\Q")); + + if (en_pos || en_neg) + opts.ffe = true; + } + else + { + if (!clkpol.empty()) + log_cmd_error("Options -clkpol and -match are exclusive!\n"); + if (!enpol.empty()) + log_cmd_error("Options -enpol and -match are exclusive!\n"); + if (opts.params) + log_cmd_error("Options -params and -match are exclusive!\n"); + } + + int dff_count = 0; + int shreg_count = 0; + + for (auto module : design->selected_modules()) { + ShregmapWorker worker(module, opts); + dff_count += worker.dff_count; + shreg_count += worker.shreg_count; + } + + log("Converted %d dff cells into %d shift registers.\n", dff_count, shreg_count); + + if (opts.tech != nullptr) { + delete opts.tech; + opts.tech = nullptr; + } + } +} ShregmapPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc index 9cea5f45d..0fb647344 100644 --- a/passes/techmap/simplemap.cc +++ b/passes/techmap/simplemap.cc @@ -2,11 +2,11 @@ * 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 @@ -35,6 +35,7 @@ void simplemap_not(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_"); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a[i]); gate->setPort("\\Y", sig_y[i]); } @@ -65,6 +66,7 @@ void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_"); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_t[i]); gate->setPort("\\Y", sig_y[i]); } @@ -81,6 +83,7 @@ void simplemap_bitop(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a[i]); gate->setPort("\\B", sig_b[i]); gate->setPort("\\Y", sig_y[i]); @@ -94,7 +97,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell) if (sig_y.size() == 0) return; - + if (sig_a.size() == 0) { if (cell->type == "$reduce_and") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(1, sig_y.size()))); if (cell->type == "$reduce_or") module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size()))); @@ -131,6 +134,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell) } RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a[i]); gate->setPort("\\B", sig_a[i+1]); gate->setPort("\\Y", sig_t[i/2]); @@ -143,6 +147,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell) if (cell->type == "$reduce_xnor") { RTLIL::SigSpec sig_t = module->addWire(NEW_ID); RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_"); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a); gate->setPort("\\Y", sig_t); last_output_cell = gate; @@ -156,7 +161,7 @@ void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell) } } -static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig) +static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig, RTLIL::Cell *cell) { while (sig.size() > 1) { @@ -170,6 +175,7 @@ static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig) } RTLIL::Cell *gate = module->addCell(NEW_ID, "$_OR_"); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig[i]); gate->setPort("\\B", sig[i+1]); gate->setPort("\\Y", sig_t[i/2]); @@ -185,19 +191,20 @@ static void logic_reduce(RTLIL::Module *module, RTLIL::SigSpec &sig) void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell) { RTLIL::SigSpec sig_a = cell->getPort("\\A"); - logic_reduce(module, sig_a); + logic_reduce(module, sig_a, cell); RTLIL::SigSpec sig_y = cell->getPort("\\Y"); if (sig_y.size() == 0) return; - + if (sig_y.size() > 1) { module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1))); sig_y = sig_y.extract(0, 1); } RTLIL::Cell *gate = module->addCell(NEW_ID, "$_NOT_"); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a); gate->setPort("\\Y", sig_y); } @@ -205,16 +212,16 @@ void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell) void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell) { RTLIL::SigSpec sig_a = cell->getPort("\\A"); - logic_reduce(module, sig_a); + logic_reduce(module, sig_a, cell); RTLIL::SigSpec sig_b = cell->getPort("\\B"); - logic_reduce(module, sig_b); + logic_reduce(module, sig_b, cell); RTLIL::SigSpec sig_y = cell->getPort("\\Y"); if (sig_y.size() == 0) return; - + if (sig_y.size() > 1) { module->connect(RTLIL::SigSig(sig_y.extract(1, sig_y.size()-1), RTLIL::SigSpec(0, sig_y.size()-1))); sig_y = sig_y.extract(0, 1); @@ -226,6 +233,7 @@ void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell) log_assert(!gate_type.empty()); RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a); gate->setPort("\\B", sig_b); gate->setPort("\\Y", sig_y); @@ -239,18 +247,21 @@ 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); module->remove(xor_cell); RTLIL::SigSpec reduce_out = is_ne ? sig_y : module->addWire(NEW_ID); RTLIL::Cell *reduce_cell = module->addReduceOr(NEW_ID, xor_out, reduce_out); + reduce_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); simplemap_reduce(module, reduce_cell); module->remove(reduce_cell); if (!is_ne) { RTLIL::Cell *not_cell = module->addLogicNot(NEW_ID, reduce_out, sig_y); + not_cell->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); simplemap_lognot(module, not_cell); module->remove(not_cell); } @@ -264,6 +275,7 @@ void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, "$_MUX_"); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\A", sig_a[i]); gate->setPort("\\B", sig_b[i]); gate->setPort("\\S", cell->getPort("\\S")); @@ -271,6 +283,74 @@ void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell) } } +void simplemap_tribuf(RTLIL::Module *module, RTLIL::Cell *cell) +{ + RTLIL::SigSpec sig_a = cell->getPort("\\A"); + RTLIL::SigSpec sig_e = cell->getPort("\\EN"); + RTLIL::SigSpec sig_y = cell->getPort("\\Y"); + + for (int i = 0; i < GetSize(sig_y); i++) { + 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); + gate->setPort("\\Y", sig_y[i]); + } +} + +void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell) +{ + SigSpec lut_ctrl = cell->getPort("\\A"); + SigSpec lut_data = cell->getParam("\\LUT"); + lut_data.extend_u0(1 << cell->getParam("\\WIDTH").as_int()); + + for (int idx = 0; GetSize(lut_data) > 1; idx++) { + SigSpec sig_s = lut_ctrl[idx]; + SigSpec new_lut_data = module->addWire(NEW_ID, GetSize(lut_data)/2); + for (int i = 0; i < GetSize(lut_data); i += 2) { + RTLIL::Cell *gate = module->addCell(NEW_ID, "$_MUX_"); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); + gate->setPort("\\A", lut_data[i]); + gate->setPort("\\B", lut_data[i+1]); + gate->setPort("\\S", lut_ctrl[idx]); + gate->setPort("\\Y", new_lut_data[i/2]); + } + lut_data = new_lut_data; + } + + module->connect(cell->getPort("\\Y"), lut_data); +} + +void simplemap_sop(RTLIL::Module *module, RTLIL::Cell *cell) +{ + SigSpec ctrl = cell->getPort("\\A"); + SigSpec table = cell->getParam("\\TABLE"); + + int width = cell->getParam("\\WIDTH").as_int(); + int depth = cell->getParam("\\DEPTH").as_int(); + table.extend_u0(2 * width * depth); + + SigSpec products; + + for (int i = 0; i < depth; i++) { + SigSpec in, pat; + for (int j = 0; j < width; j++) { + if (table[2*i*width + 2*j + 0] == State::S1) { + in.append(ctrl[j]); + pat.append(State::S0); + } + if (table[2*i*width + 2*j + 1] == State::S1) { + in.append(ctrl[j]); + pat.append(State::S1); + } + } + + products.append(GetSize(in) > 0 ? module->Eq(NEW_ID, in, pat) : State::S1); + } + + module->connect(cell->getPort("\\Y"), module->ReduceOr(NEW_ID, products)); +} + void simplemap_slice(RTLIL::Module *module, RTLIL::Cell *cell) { int offset = cell->parameters.at("\\OFFSET").as_int(); @@ -301,6 +381,7 @@ void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < width; i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\S", sig_s[i]); gate->setPort("\\R", sig_r[i]); gate->setPort("\\Q", sig_q[i]); @@ -320,6 +401,7 @@ void simplemap_dff(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < width; i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\C", sig_clk); gate->setPort("\\D", sig_d[i]); gate->setPort("\\Q", sig_q[i]); @@ -341,6 +423,7 @@ void simplemap_dffe(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < width; i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\C", sig_clk); gate->setPort("\\E", sig_en); gate->setPort("\\D", sig_d[i]); @@ -365,6 +448,7 @@ void simplemap_dffsr(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < width; i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\C", sig_clk); gate->setPort("\\S", sig_s[i]); gate->setPort("\\R", sig_r[i]); @@ -393,6 +477,7 @@ void simplemap_adff(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < width; i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, rst_val.at(i) == RTLIL::State::S1 ? gate_type_1 : gate_type_0); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\C", sig_clk); gate->setPort("\\R", sig_rst); gate->setPort("\\D", sig_d[i]); @@ -413,6 +498,7 @@ void simplemap_dlatch(RTLIL::Module *module, RTLIL::Cell *cell) for (int i = 0; i < width; i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute("\\src", cell->get_strpool_attribute("\\src")); gate->setPort("\\E", sig_en); gate->setPort("\\D", sig_d[i]); gate->setPort("\\Q", sig_q[i]); @@ -440,6 +526,9 @@ void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTL mappers["$ne"] = simplemap_eqne; mappers["$nex"] = simplemap_eqne; mappers["$mux"] = simplemap_mux; + mappers["$tribuf"] = simplemap_tribuf; + mappers["$lut"] = simplemap_lut; + mappers["$sop"] = simplemap_sop; mappers["$slice"] = simplemap_slice; mappers["$concat"] = simplemap_concat; mappers["$sr"] = simplemap_sr; @@ -479,13 +568,13 @@ struct SimplemapPass : public Pass { log("\n"); log(" $not, $pos, $and, $or, $xor, $xnor\n"); log(" $reduce_and, $reduce_or, $reduce_xor, $reduce_xnor, $reduce_bool\n"); - log(" $logic_not, $logic_and, $logic_or, $mux\n"); + log(" $logic_not, $logic_and, $logic_or, $mux, $tribuf\n"); log(" $sr, $dff, $dffsr, $adff, $dlatch\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing SIMPLEMAP pass (map simple cells to gate primitives).\n"); + log_header(design, "Executing SIMPLEMAP pass (map simple cells to gate primitives).\n"); extra_args(args, 1, design); std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers; @@ -507,5 +596,5 @@ struct SimplemapPass : public Pass { } } } SimplemapPass; - + PRIVATE_NAMESPACE_END diff --git a/passes/techmap/simplemap.h b/passes/techmap/simplemap.h index dc2a395d3..c2d73ea79 100644 --- a/passes/techmap/simplemap.h +++ b/passes/techmap/simplemap.h @@ -2,11 +2,11 @@ * 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 @@ -31,6 +31,7 @@ extern void simplemap_reduce(RTLIL::Module *module, RTLIL::Cell *cell); extern void simplemap_lognot(RTLIL::Module *module, RTLIL::Cell *cell); extern void simplemap_logbin(RTLIL::Module *module, RTLIL::Cell *cell); extern void simplemap_mux(RTLIL::Module *module, RTLIL::Cell *cell); +extern void simplemap_lut(RTLIL::Module *module, RTLIL::Cell *cell); extern void simplemap_slice(RTLIL::Module *module, RTLIL::Cell *cell); extern void simplemap_concat(RTLIL::Module *module, RTLIL::Cell *cell); extern void simplemap_sr(RTLIL::Module *module, RTLIL::Cell *cell); diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index 521ac61a0..1ab6df1dc 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -2,11 +2,11 @@ * 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 @@ -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(); @@ -66,6 +66,11 @@ struct TechmapWorker std::map<std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>>, RTLIL::Module*> techmap_cache; std::map<RTLIL::Module*, bool> techmap_do_cache; std::set<RTLIL::Module*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Module>> module_queue; + dict<Module*, SigMap> sigmaps; + + pool<IdString> flatten_do_list; + pool<IdString> flatten_done_list; + pool<Cell*> flatten_keep_list; struct TechmapWireData { RTLIL::Wire *wire; @@ -154,9 +159,10 @@ struct TechmapWorker void techmap_module_worker(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl) { if (tpl->processes.size() != 0) { - log("Technology map yielded processes:\n"); + log("Technology map yielded processes:"); for (auto &it : tpl->processes) - log(" %s",RTLIL::id2cstr(it.first)); + log(" %s",RTLIL::id2cstr(it.first)); + log("\n"); if (autoproc_mode) { Pass::call_on_module(tpl->design, tpl, "proc"); log_assert(GetSize(tpl->processes) == 0); @@ -165,7 +171,10 @@ struct TechmapWorker } std::string orig_cell_name; + pool<string> extra_src_attrs; + if (!flatten_mode) + { for (auto &it : tpl->cells_) if (it.first == "\\_TECHMAP_REPLACE_") { orig_cell_name = cell->name.str(); @@ -173,6 +182,9 @@ struct TechmapWorker break; } + extra_src_attrs = cell->get_strpool_attribute("\\src"); + } + dict<IdString, IdString> memory_renames; for (auto &it : tpl->memories) { @@ -184,6 +196,8 @@ struct TechmapWorker m->start_offset = it.second->start_offset; m->size = it.second->size; m->attributes = it.second->attributes; + if (m->attributes.count("\\src")) + m->add_strpool_attribute("\\src", extra_src_attrs); module->memories[m->name] = m; memory_renames[it.first] = m->name; design->select(module, m); @@ -202,12 +216,28 @@ struct TechmapWorker w->port_id = 0; if (it.second->get_bool_attribute("\\_techmap_special_")) w->attributes.clear(); + if (w->attributes.count("\\src")) + w->add_strpool_attribute("\\src", extra_src_attrs); design->select(module, w); } + SigMap tpl_sigmap(tpl); + pool<SigBit> tpl_written_bits; + + for (auto &it1 : tpl->cells_) + for (auto &it2 : it1.second->connections_) + if (it1.second->output(it2.first)) + for (auto bit : tpl_sigmap(it2.second)) + tpl_written_bits.insert(bit); + for (auto &it1 : tpl->connections_) + for (auto bit : tpl_sigmap(it1.first)) + tpl_written_bits.insert(bit); + SigMap port_signal_map; + SigSig port_signal_assign; - for (auto &it : cell->connections()) { + for (auto &it : cell->connections()) + { RTLIL::IdString portname = it.first; if (positional_ports.count(portname) > 0) portname = positional_ports.at(portname); @@ -216,33 +246,76 @@ struct TechmapWorker log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", portname.c_str(), cell->name.c_str(), tpl->name.c_str()); continue; } + RTLIL::Wire *w = tpl->wires_.at(portname); - RTLIL::SigSig c; - if (w->port_output) { + RTLIL::SigSig c, extra_connect; + + if (w->port_output && !w->port_input) { c.first = it.second; c.second = RTLIL::SigSpec(w); apply_prefix(cell->name.str(), c.second, module); - } else { + extra_connect.first = c.second; + extra_connect.second = c.first; + } else if (!w->port_output && w->port_input) { c.first = RTLIL::SigSpec(w); c.second = it.second; apply_prefix(cell->name.str(), c.first, module); + extra_connect.first = c.first; + extra_connect.second = c.second; + } else { + SigSpec sig_tpl = w, sig_tpl_pf = w, sig_mod = it.second; + apply_prefix(cell->name.str(), sig_tpl_pf, module); + for (int i = 0; i < GetSize(sig_tpl) && i < GetSize(sig_mod); i++) { + if (tpl_written_bits.count(tpl_sigmap(sig_tpl[i]))) { + c.first.append(sig_mod[i]); + c.second.append(sig_tpl_pf[i]); + } else { + c.first.append(sig_tpl_pf[i]); + c.second.append(sig_mod[i]); + } + } + extra_connect.first = sig_tpl_pf; + extra_connect.second = sig_mod; } + if (c.second.size() > c.first.size()) c.second.remove(c.first.size(), c.second.size() - c.first.size()); + if (c.second.size() < c.first.size()) c.second.append(RTLIL::SigSpec(RTLIL::State::S0, c.first.size() - c.second.size())); + log_assert(c.first.size() == c.second.size()); - if (flatten_mode) { + + if (flatten_mode) + { // more conservative approach: // connect internal and external wires + + if (sigmaps.count(module) == 0) + sigmaps[module].set(module); + + if (sigmaps.at(module)(c.first).has_const()) + log_error("Mismatch in directionality for cell port %s.%s.%s: %s <= %s\n", + log_id(module), log_id(cell), log_id(it.first), log_signal(c.first), log_signal(c.second)); + module->connect(c); - } else { + } + else + { // approach that yields nicer outputs: // replace internal wires that are connected to external wires + if (w->port_output) port_signal_map.add(c.second, c.first); else port_signal_map.add(c.first, c.second); + + for (auto &attr : w->attributes) { + if (attr.first == "\\src") + continue; + module->connect(extra_connect); + break; + } } } @@ -266,11 +339,14 @@ struct TechmapWorker port_signal_map.apply(it2.second); } - if (c->type == "$memrd" || c->type == "$memwr") { + 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())); } + + if (c->attributes.count("\\src")) + c->add_strpool_attribute("\\src", extra_src_attrs); } for (auto &it : tpl->connections()) { @@ -317,6 +393,22 @@ struct TechmapWorker continue; } + if (flatten_mode) { + bool keepit = cell->get_bool_attribute("\\keep_hierarchy"); + for (auto &tpl_name : celltypeMap.at(cell_type)) + if (map->modules_[tpl_name]->get_bool_attribute("\\keep_hierarchy")) + keepit = true; + if (keepit) { + if (!flatten_keep_list[cell]) { + log("Keeping %s.%s (found keep_hierarchy property).\n", log_id(module), log_id(cell)); + flatten_keep_list.insert(cell); + } + if (!flatten_done_list[cell->type]) + flatten_do_list.insert(cell->type); + continue; + } + } + for (auto &conn : cell->connections()) { RTLIL::SigSpec sig = sigmap(conn.second); @@ -715,7 +807,7 @@ struct TechmapWorker if (recursive_mode) { if (log_continue) { - log_header("Continuing TECHMAP pass.\n"); + log_header(design, "Continuing TECHMAP pass.\n"); log_continue = false; } while (techmap_module(map, tpl, map, handled_cells, celltypeMap, true)) { } @@ -726,7 +818,7 @@ struct TechmapWorker continue; if (log_continue) { - log_header("Continuing TECHMAP pass.\n"); + log_header(design, "Continuing TECHMAP pass.\n"); log_continue = false; } @@ -769,7 +861,7 @@ struct TechmapWorker } if (log_continue) { - log_header("Continuing TECHMAP pass.\n"); + log_header(design, "Continuing TECHMAP pass.\n"); log_continue = false; } @@ -786,7 +878,7 @@ struct TechmapPass : public Pass { log(" techmap [-map filename] [selection]\n"); log("\n"); log("This pass implements a very simple technology mapper that replaces cells in\n"); - log("the design with implementations given in form of a verilog or ilang source\n"); + log("the design with implementations given in form of a Verilog or ilang source\n"); log("file.\n"); log("\n"); log(" -map filename\n"); @@ -798,11 +890,6 @@ struct TechmapPass : public Pass { log(" -map %%<design-name>\n"); log(" like -map above, but with an in-memory design instead of a file.\n"); log("\n"); - log(" -share_map filename\n"); - log(" like -map, but look for the file in the share directory (where the\n"); - log(" yosys data files are). this is mainly used internally when techmap\n"); - log(" is called from other commands.\n"); - log("\n"); log(" -extern\n"); log(" load the cell implementations as separate modules into the design\n"); log(" instead of inlining them.\n"); @@ -812,7 +899,7 @@ struct TechmapPass : public Pass { log("\n"); log(" -recursive\n"); log(" instead of the iterative breadth-first algorithm use a recursive\n"); - log(" depth-first algorithm. both methods should yield equivialent results,\n"); + log(" depth-first algorithm. both methods should yield equivalent results,\n"); log(" but may differ in performance.\n"); log("\n"); log(" -autoproc\n"); @@ -824,8 +911,8 @@ struct TechmapPass : public Pass { log(" as final cell types by this mode.\n"); log("\n"); log(" -D <define>, -I <incdir>\n"); - log(" this options are passed as-is to the verilog frontend for loading the\n"); - log(" map file. Note that the verilog frontend is also called with the\n"); + log(" this options are passed as-is to the Verilog frontend for loading the\n"); + log(" map file. Note that the Verilog frontend is also called with the\n"); log(" '-ignore_redef' option set.\n"); log("\n"); log("When a module in the map file has the 'techmap_celltype' attribute set, it will\n"); @@ -871,7 +958,7 @@ struct TechmapPass : public Pass { log(" of constant inputs and shorted inputs at this point and import the\n"); log(" constant and connected bits into the map module. All further commands\n"); log(" are executed in this copy. This is a very convenient way of creating\n"); - log(" optimizied specializations of techmap modules without using the special\n"); + log(" optimized specializations of techmap modules without using the special\n"); log(" parameters described below.\n"); log("\n"); log(" A _TECHMAP_DO_* command may start with the special token 'RECURSION; '.\n"); @@ -907,17 +994,17 @@ struct TechmapPass : public Pass { log("constant value.\n"); log("\n"); log("A cell with the name _TECHMAP_REPLACE_ in the map file will inherit the name\n"); - log("of the cell that is beeing replaced.\n"); + log("of the cell that is being replaced.\n"); log("\n"); log("See 'help extract' for a pass that does the opposite thing.\n"); log("\n"); log("See 'help flatten' for a pass that does flatten the design (which is\n"); - log("esentially techmap but using the design itself as map library).\n"); + log("essentially techmap but using the design itself as map library).\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing TECHMAP pass (map to technology primitives).\n"); + log_header(design, "Executing TECHMAP pass (map to technology primitives).\n"); log_push(); TechmapWorker worker; @@ -930,14 +1017,7 @@ struct TechmapPass : public Pass { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-map" && argidx+1 < args.size()) { - if (args[argidx+1].substr(0, 2) == "+/") - map_files.push_back(proc_share_dirname() + args[++argidx].substr(2)); - else - map_files.push_back(args[++argidx]); - continue; - } - if (args[argidx] == "-share_map" && argidx+1 < args.size()) { - map_files.push_back(proc_share_dirname() + args[++argidx]); + map_files.push_back(args[++argidx]); continue; } if (args[argidx] == "-max_iter" && argidx+1 < args.size()) { @@ -988,20 +1068,13 @@ struct TechmapPass : public Pass { map->add(mod->clone()); } else { std::ifstream f; + rewrite_filename(fn); f.open(fn.c_str()); if (f.fail()) log_cmd_error("Can't open map file `%s'\n", fn.c_str()); Frontend::frontend_call(map, &f, fn, (fn.size() > 3 && fn.substr(fn.size()-3) == ".il") ? "ilang" : verilog_frontend); } - dict<RTLIL::IdString, RTLIL::Module*> modules_new; - for (auto &it : map->modules_) { - if (it.first.substr(0, 2) == "\\$") - it.second->name = it.first.substr(1); - modules_new[it.second->name] = it.second; - } - map->modules_.swap(modules_new); - std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap; for (auto &it : map->modules_) { if (it.second->attributes.count("\\techmap_celltype") && !it.second->attributes.at("\\techmap_celltype").bits.empty()) { @@ -1009,8 +1082,12 @@ struct TechmapPass : public Pass { for (char *q = strtok(p, " \t\r\n"); q; q = strtok(NULL, " \t\r\n")) celltypeMap[RTLIL::escape_id(q)].insert(it.first); free(p); - } else - celltypeMap[it.first].insert(it.first); + } else { + string module_name = it.first.str(); + if (module_name.substr(0, 2) == "\\$") + module_name = module_name.substr(1); + celltypeMap[module_name].insert(it.first); + } } for (auto module : design->modules()) @@ -1040,7 +1117,7 @@ struct TechmapPass : public Pass { log_pop(); } } TechmapPass; - + struct FlattenPass : public Pass { FlattenPass() : Pass("flatten", "flatten design") { } virtual void help() @@ -1050,13 +1127,16 @@ struct FlattenPass : public Pass { log(" flatten [selection]\n"); log("\n"); log("This pass flattens the design by replacing cells by their implementation. This\n"); - log("pass is very simmilar to the 'techmap' pass. The only difference is that this\n"); + log("pass is very similar to the 'techmap' pass. The only difference is that this\n"); log("pass is using the current design as mapping library.\n"); log("\n"); + log("Cells and/or modules with the 'keep_hierarchy' attribute set will not be\n"); + log("flattened by this command.\n"); + log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - log_header("Executing FLATTEN pass (flatten design).\n"); + log_header(design, "Executing FLATTEN pass (flatten design).\n"); log_push(); extra_args(args, 1, design); @@ -1065,8 +1145,8 @@ struct FlattenPass : public Pass { worker.flatten_mode = true; std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap; - for (auto &it : design->modules_) - celltypeMap[it.first].insert(it.first); + for (auto module : design->modules()) + celltypeMap[module->name].insert(module->name); RTLIL::Module *top_mod = NULL; if (design->full_selection()) @@ -1074,26 +1154,40 @@ struct FlattenPass : public Pass { if (mod->get_bool_attribute("\\top")) top_mod = mod; - bool did_something = true; std::set<RTLIL::Cell*> handled_cells; - while (did_something) { - did_something = false; - if (top_mod != NULL) { - if (worker.techmap_module(design, top_mod, design, handled_cells, celltypeMap, false)) - did_something = true; - } else { - for (auto mod : design->modules()) - if (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) - did_something = true; + if (top_mod != NULL) { + worker.flatten_do_list.insert(top_mod->name); + while (!worker.flatten_do_list.empty()) { + auto mod = design->module(*worker.flatten_do_list.begin()); + while (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) { } + worker.flatten_done_list.insert(mod->name); + worker.flatten_do_list.erase(mod->name); } + } else { + for (auto mod : vector<Module*>(design->modules())) + while (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) { } } log("No more expansions possible.\n"); - if (top_mod != NULL) { + if (top_mod != NULL) + { + pool<RTLIL::IdString> used_modules, new_used_modules; + new_used_modules.insert(top_mod->name); + while (!new_used_modules.empty()) { + pool<RTLIL::IdString> queue; + queue.swap(new_used_modules); + for (auto modname : queue) + used_modules.insert(modname); + for (auto modname : queue) + for (auto cell : design->module(modname)->cells()) + if (design->module(cell->type) && !used_modules[cell->type]) + new_used_modules.insert(cell->type); + } + dict<RTLIL::IdString, RTLIL::Module*> new_modules; - for (auto mod : design->modules()) - if (mod == top_mod || mod->get_bool_attribute("\\blackbox")) { + for (auto mod : vector<Module*>(design->modules())) + if (used_modules[mod->name] || mod->get_bool_attribute("\\blackbox")) { new_modules[mod->name] = mod; } else { log("Deleting now unused module %s.\n", log_id(mod)); diff --git a/passes/techmap/tribuf.cc b/passes/techmap/tribuf.cc new file mode 100644 index 000000000..03629082c --- /dev/null +++ b/passes/techmap/tribuf.cc @@ -0,0 +1,186 @@ +/* + * 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 TribufConfig { + bool merge_mode; + bool logic_mode; + + TribufConfig() { + merge_mode = false; + logic_mode = false; + } +}; + +struct TribufWorker { + Module *module; + SigMap sigmap; + const TribufConfig &config; + + TribufWorker(Module *module, const TribufConfig &config) : module(module), sigmap(module), config(config) + { + } + + static bool is_all_z(SigSpec sig) + { + for (auto bit : sig) + if (bit != State::Sz) + return false; + return true; + } + + void run() + { + dict<SigSpec, vector<Cell*>> tribuf_cells; + pool<SigBit> output_bits; + + if (config.logic_mode) + for (auto wire : module->wires()) + if (wire->port_output) + for (auto bit : sigmap(wire)) + output_bits.insert(bit); + + for (auto cell : module->selected_cells()) + { + if (cell->type == "$tribuf") + tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell); + + if (cell->type == "$_TBUF_") + tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell); + + if (cell->type.in("$mux", "$_MUX_")) + { + IdString en_port = cell->type == "$mux" ? "\\EN" : "\\E"; + IdString tri_type = cell->type == "$mux" ? "$tribuf" : "$_TBUF_"; + + if (is_all_z(cell->getPort("\\A")) && is_all_z(cell->getPort("\\B"))) { + module->remove(cell); + continue; + } + + if (is_all_z(cell->getPort("\\A"))) { + cell->setPort("\\A", cell->getPort("\\B")); + cell->setPort(en_port, cell->getPort("\\S")); + cell->unsetPort("\\B"); + cell->unsetPort("\\S"); + cell->type = tri_type; + tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell); + continue; + } + + if (is_all_z(cell->getPort("\\B"))) { + cell->setPort(en_port, module->Not(NEW_ID, cell->getPort("\\S"))); + cell->unsetPort("\\B"); + cell->unsetPort("\\S"); + cell->type = tri_type; + tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell); + continue; + } + } + } + + if (config.merge_mode || config.logic_mode) + { + for (auto &it : tribuf_cells) + { + bool no_tribuf = false; + + if (config.logic_mode) { + no_tribuf = true; + for (auto bit : it.first) + if (output_bits.count(bit)) + no_tribuf = false; + } + + if (GetSize(it.second) <= 1 && !no_tribuf) + continue; + + SigSpec pmux_b, pmux_s; + for (auto cell : it.second) { + if (cell->type == "$tribuf") + pmux_s.append(cell->getPort("\\EN")); + else + pmux_s.append(cell->getPort("\\E")); + pmux_b.append(cell->getPort("\\A")); + module->remove(cell); + } + + SigSpec muxout = GetSize(pmux_s) > 1 ? module->Pmux(NEW_ID, SigSpec(State::Sx, GetSize(it.first)), pmux_b, pmux_s) : pmux_b; + + if (no_tribuf) + module->connect(it.first, muxout); + else + module->addTribuf(NEW_ID, muxout, module->ReduceOr(NEW_ID, pmux_s), it.first); + } + } + } +}; + +struct TribufPass : public Pass { + TribufPass() : Pass("tribuf", "infer tri-state buffers") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" tribuf [options] [selection]\n"); + log("\n"); + log("This pass transforms $mux cells with 'z' inputs to tristate buffers.\n"); + log("\n"); + log(" -merge\n"); + log(" merge multiple tri-state buffers driving the same net\n"); + log(" into a single buffer.\n"); + log("\n"); + log(" -logic\n"); + log(" convert tri-state buffers that do not drive output ports\n"); + log(" to non-tristate logic. this option implies -merge.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + TribufConfig config; + + log_header(design, "Executing TRIBUF pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-merge") { + config.merge_mode = true; + continue; + } + if (args[argidx] == "-logic") { + config.logic_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) { + TribufWorker worker(module, config); + worker.run(); + } + } +} TribufPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/tests/test_abcloop.cc b/passes/tests/test_abcloop.cc index 753fa7bf2..09cb41954 100644 --- a/passes/tests/test_abcloop.cc +++ b/passes/tests/test_abcloop.cc @@ -127,9 +127,9 @@ static void test_abcloop() module->fixup_ports(); Pass::call(design, "clean"); - ezDefaultSAT ez; + ezSatPtr ez; SigMap sigmap(module); - SatGen satgen(&ez, &sigmap); + SatGen satgen(ez.get(), &sigmap); for (auto c : module->cells()) { bool ok YS_ATTRIBUTE(unused) = satgen.importCell(c); @@ -137,7 +137,7 @@ static void test_abcloop() } std::vector<int> in_vec = satgen.importSigSpec(in_sig); - std::vector<int> inverse_in_vec = ez.vec_not(in_vec); + std::vector<int> inverse_in_vec = ez->vec_not(in_vec); std::vector<int> out_vec = satgen.importSigSpec(out_sig); @@ -148,7 +148,7 @@ static void test_abcloop() assumptions.push_back((i & (1 << j)) ? in_vec.at(j) : inverse_in_vec.at(j)); std::vector<bool> results; - if (!ez.solve(out_vec, results, assumptions)) { + if (!ez->solve(out_vec, results, assumptions)) { log("No stable solution for input %d found -> recreate module.\n", i); goto recreate_module; } @@ -156,10 +156,10 @@ static void test_abcloop() for (int j = 0; j < 4; j++) truthtab[i][j] = results[j]; - assumptions.push_back(ez.vec_ne(out_vec, ez.vec_const(results))); + assumptions.push_back(ez->vec_ne(out_vec, ez->vec_const(results))); std::vector<bool> results2; - if (ez.solve(out_vec, results2, assumptions)) { + if (ez->solve(out_vec, results2, assumptions)) { log("Two stable solutions for input %d found -> recreate module.\n", i); goto recreate_module; } @@ -177,9 +177,9 @@ static void test_abcloop() log("\n"); log("Pre- and post-abc truth table:\n"); - ezDefaultSAT ez; + ezSatPtr ez; SigMap sigmap(module); - SatGen satgen(&ez, &sigmap); + SatGen satgen(ez.get(), &sigmap); for (auto c : module->cells()) { bool ok YS_ATTRIBUTE(unused) = satgen.importCell(c); @@ -187,7 +187,7 @@ static void test_abcloop() } std::vector<int> in_vec = satgen.importSigSpec(in_sig); - std::vector<int> inverse_in_vec = ez.vec_not(in_vec); + std::vector<int> inverse_in_vec = ez->vec_not(in_vec); std::vector<int> out_vec = satgen.importSigSpec(out_sig); @@ -204,7 +204,7 @@ static void test_abcloop() truthtab2[i][j] = truthtab[i][j]; std::vector<bool> results; - if (!ez.solve(out_vec, results, assumptions)) { + if (!ez->solve(out_vec, results, assumptions)) { log("No stable solution for input %d found.\n", i); found_error = true; continue; @@ -213,10 +213,10 @@ static void test_abcloop() for (int j = 0; j < 4; j++) truthtab2[i][j] = results[j]; - assumptions.push_back(ez.vec_ne(out_vec, ez.vec_const(results))); + assumptions.push_back(ez->vec_ne(out_vec, ez->vec_const(results))); std::vector<bool> results2; - if (ez.solve(out_vec, results2, assumptions)) { + if (ez->solve(out_vec, results2, assumptions)) { log("Two stable solutions for input %d found -> recreate module.\n", i); found_error = true; } diff --git a/passes/tests/test_autotb.cc b/passes/tests/test_autotb.cc index 74ee0f5a9..59de111c2 100644 --- a/passes/tests/test_autotb.cc +++ b/passes/tests/test_autotb.cc @@ -2,11 +2,11 @@ * 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 @@ -73,9 +73,14 @@ static std::string idy(std::string str1, std::string str2 = std::string(), std:: static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) { + f << stringf("`ifndef outfile\n"); + f << stringf("\t`define outfile \"/dev/stdout\"\n"); + f << stringf("`endif\n"); + f << stringf("module testbench;\n\n"); - f << stringf("integer i;\n\n"); + f << stringf("integer i;\n"); + f << stringf("integer file;\n\n"); f << stringf("reg [31:0] xorshift128_x = 123456789;\n"); f << stringf("reg [31:0] xorshift128_y = 362436069;\n"); @@ -195,7 +200,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) f << stringf(" } = {"); for (auto it = signal_clk.begin(); it != signal_clk.end(); it++) f << stringf("%s %s", it == signal_clk.begin() ? "" : ",", it->first.c_str()); - f << stringf(" } ^ (%d'b1 << (xorshift128_w %% %d));\n", total_clock_bits, total_clock_bits); + f << stringf(" } ^ (%d'b1 << (xorshift128_w %% %d));\n", total_clock_bits, total_clock_bits + 1); } f << stringf("end\n"); f << stringf("endtask\n\n"); @@ -206,7 +211,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) f << stringf("task %s;\n", idy(mod->name.str(), "print_status").c_str()); f << stringf("begin\n"); - f << stringf("\t$display(\"#OUT# %%b %%b %%b %%t %%d\", {"); + f << stringf("\t$fdisplay(file, \"#OUT# %%b %%b %%b %%t %%d\", {"); if (signal_in.size()) for (auto it = signal_in.begin(); it != signal_in.end(); it++) { f << stringf("%s %s", it == signal_in.begin() ? "" : ",", it->first.c_str()); @@ -218,7 +223,8 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) if (len > 0) header2 += shorthand, len--; header1.push_back(" " + it->first); - header1.back()[0] = shorthand++; + header1.back()[0] = shorthand; + shorthand = shorthand == 'Z' ? 'A' : shorthand+1; } else { f << stringf(" 1'bx"); @@ -237,7 +243,8 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) if (len > 0) header2 += shorthand, len--; header1.push_back(" " + it->first); - header1.back()[0] = shorthand++; + header1.back()[0] = shorthand; + shorthand = shorthand == 'Z' ? 'A' : shorthand+1; } } else { f << stringf(" 1'bx"); @@ -256,7 +263,8 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) if (len > 0) header2 += shorthand, len--; header1.push_back(" " + it->first); - header1.back()[0] = shorthand++; + header1.back()[0] = shorthand; + shorthand = shorthand == 'Z' ? 'A' : shorthand+1; } } else { f << stringf(" 1'bx"); @@ -268,17 +276,17 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) f << stringf("task %s;\n", idy(mod->name.str(), "print_header").c_str()); f << stringf("begin\n"); - f << stringf("\t$display(\"#OUT#\");\n"); + f << stringf("\t$fdisplay(file, \"#OUT#\");\n"); for (auto &hdr : header1) - f << stringf("\t$display(\"#OUT# %s\");\n", hdr.c_str()); - f << stringf("\t$display(\"#OUT#\");\n"); - f << stringf("\t$display(\"#OUT# %s\");\n", header2.c_str()); + f << stringf("\t$fdisplay(file, \"#OUT# %s\");\n", hdr.c_str()); + f << stringf("\t$fdisplay(file, \"#OUT#\");\n"); + f << stringf("\t$fdisplay(file, \"#OUT# %s\");\n", header2.c_str()); f << stringf("end\n"); f << stringf("endtask\n\n"); f << stringf("task %s;\n", idy(mod->name.str(), "test").c_str()); f << stringf("begin\n"); - f << stringf("\t$display(\"#OUT#\\n#OUT# ==== %s ====\");\n", idy(mod->name.str()).c_str()); + f << stringf("\t$fdisplay(file, \"#OUT#\\n#OUT# ==== %s ====\");\n", idy(mod->name.str()).c_str()); f << stringf("\t%s;\n", idy(mod->name.str(), "reset").c_str()); f << stringf("\tfor (i=0; i<%d; i=i+1) begin\n", num_iter); f << stringf("\t\tif (i %% 20 == 0) %s;\n", idy(mod->name.str(), "print_header").c_str()); @@ -293,9 +301,11 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter) f << stringf("initial begin\n"); f << stringf("\t// $dumpfile(\"testbench.vcd\");\n"); f << stringf("\t// $dumpvars(0, testbench);\n"); + f << stringf("\tfile = $fopen(`outfile);\n"); for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) if (!it->second->get_bool_attribute("\\gentb_skip")) f << stringf("\t%s;\n", idy(it->first.str(), "test").c_str()); + f << stringf("\t$fclose(file);\n"); f << stringf("\t$finish;\n"); f << stringf("end\n\n"); @@ -310,7 +320,7 @@ struct TestAutotbBackend : public Backend { log("\n"); log(" test_autotb [options] [filename]\n"); log("\n"); - log("Automatically create primitive verilog test benches for all modules in the\n"); + log("Automatically create primitive Verilog test benches for all modules in the\n"); log("design. The generated testbenches toggle the input pins of the module in\n"); log("a semi-random manner and dumps the resulting output signals.\n"); log("\n"); @@ -326,14 +336,14 @@ struct TestAutotbBackend : public Backend { log("low in order to explore more inner states in a state machine.\n"); log("\n"); log(" -n <int>\n"); - log(" number of iterations the test bench shuld run (default = 1000)\n"); + log(" number of iterations the test bench should run (default = 1000)\n"); log("\n"); } virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) { int num_iter = 1000; - log_header("Executing TEST_AUTOTB backend (auto-generate pseudo-random test benches).\n"); + log_header(design, "Executing TEST_AUTOTB backend (auto-generate pseudo-random test benches).\n"); int argidx; for (argidx = 1; argidx < GetSize(args); argidx++) @@ -349,6 +359,6 @@ struct TestAutotbBackend : public Backend { autotest(*f, design, num_iter); } } TestAutotbBackend; - + PRIVATE_NAMESPACE_END diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc index ea2ab1e65..8b800d414 100644 --- a/passes/tests/test_cell.cc +++ b/passes/tests/test_cell.cc @@ -164,6 +164,41 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type, cell->setParam("\\LUT", config.as_const()); } + if (cell_type == "$sop") + { + int width = 1 + xorshift32(8); + int depth = 1 + xorshift32(8); + + wire = module->addWire("\\A"); + wire->width = width; + wire->port_input = true; + cell->setPort("\\A", wire); + + wire = module->addWire("\\Y"); + wire->port_output = true; + cell->setPort("\\Y", wire); + + RTLIL::SigSpec config; + for (int i = 0; i < width*depth; i++) + switch (xorshift32(3)) { + case 0: + config.append(RTLIL::S1); + config.append(RTLIL::S0); + break; + case 1: + config.append(RTLIL::S0); + config.append(RTLIL::S1); + break; + case 2: + config.append(RTLIL::S0); + config.append(RTLIL::S0); + break; + } + + cell->setParam("\\DEPTH", depth); + cell->setParam("\\TABLE", config.as_const()); + } + if (cell_type_flags.find('A') != std::string::npos) { wire = module->addWire("\\A"); wire->width = 1 + xorshift32(8); @@ -256,7 +291,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; } @@ -278,10 +313,10 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: RTLIL::Module *gate_mod = design->module("\\gate"); ConstEval gold_ce(gold_mod), gate_ce(gate_mod); - ezDefaultSAT ez1, ez2; + ezSatPtr ez1, ez2; SigMap sigmap(gold_mod); - SatGen satgen1(&ez1, &sigmap); - SatGen satgen2(&ez2, &sigmap); + SatGen satgen1(ez1.get(), &sigmap); + SatGen satgen2(ez2.get(), &sigmap); satgen2.model_undef = true; if (!nosat) @@ -433,7 +468,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: std::vector<int> sat1_model = satgen1.importSigSpec(out_sig); std::vector<bool> sat1_model_value; - if (!ez1.solve(sat1_model, sat1_model_value, ez1.vec_eq(sat1_in_sig, sat1_in_val))) + if (!ez1->solve(sat1_model, sat1_model_value, ez1->vec_eq(sat1_in_sig, sat1_in_val))) log_error("Evaluating sat model 1 (no undef modeling) failed!\n"); if (verbose) { @@ -468,7 +503,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: std::vector<bool> sat2_model_value; - if (!ez2.solve(sat2_model, sat2_model_value, ez2.vec_eq(sat2_in_def_sig, sat2_in_def_val), ez2.vec_eq(sat2_in_undef_sig, sat2_in_undef_val))) + if (!ez2->solve(sat2_model, sat2_model_value, ez2->vec_eq(sat2_in_def_sig, sat2_in_def_val), ez2->vec_eq(sat2_in_undef_sig, sat2_in_undef_val))) log_error("Evaluating sat model 2 (undef modeling) failed!\n"); if (verbose) { @@ -534,7 +569,10 @@ struct TestCellPass : public Pass { log(" pass this option to techmap.\n"); log("\n"); log(" -simlib\n"); - log(" use \"techmap -map +/simlib.v -max_iter 2 -autoproc\"\n"); + log(" use \"techmap -D SIMLIB_NOCHECKS -map +/simlib.v -max_iter 2 -autoproc\"\n"); + log("\n"); + log(" -aigmap\n"); + log(" instead of calling \"techmap\", call \"aigmap\"\n"); log("\n"); log(" -muxdiv\n"); log(" when creating test benches with dividers, create an additional mux\n"); @@ -549,11 +587,14 @@ struct TestCellPass : public Pass { log(" -nosat\n"); log(" do not check SAT model or run SAT equivalence checking\n"); log("\n"); + log(" -noeval\n"); + log(" do not check const-eval models\n"); + log("\n"); log(" -v\n"); log(" print additional debug information to the console\n"); log("\n"); log(" -vlog {filename}\n"); - log(" create a verilog test bench to test simlib and write_verilog\n"); + log(" create a Verilog test bench to test simlib and write_verilog\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design*) @@ -567,6 +608,7 @@ struct TestCellPass : public Pass { bool verbose = false; bool constmode = false; bool nosat = false; + bool noeval = false; int argidx; for (argidx = 1; argidx < GetSize(args); argidx++) @@ -597,7 +639,11 @@ struct TestCellPass : public Pass { continue; } if (args[argidx] == "-simlib") { - techmap_cmd = "techmap -map +/simlib.v -max_iter 2 -autoproc"; + techmap_cmd = "techmap -D SIMLIB_NOCHECKS -map +/simlib.v -max_iter 2 -autoproc"; + continue; + } + if (args[argidx] == "-aigmap") { + techmap_cmd = "aigmap"; continue; } if (args[argidx] == "-muxdiv") { @@ -612,6 +658,10 @@ struct TestCellPass : public Pass { nosat = true; continue; } + if (args[argidx] == "-noeval") { + noeval = true; + continue; + } if (args[argidx] == "-v") { verbose = true; continue; @@ -682,6 +732,7 @@ struct TestCellPass : public Pass { // cell_types["$assert"] = "A"; cell_types["$lut"] = "*"; + cell_types["$sop"] = "*"; cell_types["$alu"] = "ABSY"; cell_types["$lcu"] = "*"; cell_types["$macc"] = "*"; @@ -765,7 +816,8 @@ struct TestCellPass : public Pass { Backend::backend_call(design, &vlog_file, "<test_cell -vlog>", "verilog -selected -noexpr"); uut_names.push_back(uut_name); } - run_eval_test(design, verbose, nosat, uut_name, vlog_file); + if (!noeval) + run_eval_test(design, verbose, nosat, uut_name, vlog_file); } delete design; } diff --git a/techlibs/common/.gitignore b/techlibs/common/.gitignore new file mode 100644 index 000000000..0a1e7b68d --- /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 d2ce61cf6..236d6c551 100644 --- a/techlibs/common/Makefile.inc +++ b/techlibs/common/Makefile.inc @@ -1,18 +1,27 @@ ifneq ($(SMALL),1) OBJS += techlibs/common/synth.o +OBJS += techlibs/common/prep.o endif -EXTRA_TARGETS += techlibs/common/blackbox.v +GENFILES += techlibs/common/simlib_help.inc +GENFILES += techlibs/common/simcells_help.inc -techlibs/common/blackbox.v: techlibs/common/blackbox.sed techlibs/common/simlib.v techlibs/common/simcells.v - $(P) cat techlibs/common/simlib.v techlibs/common/simcells.v | $(SED) -rf techlibs/common/blackbox.sed > techlibs/common/blackbox.v.new - $(Q) mv techlibs/common/blackbox.v.new techlibs/common/blackbox.v +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)) -$(eval $(call add_share_file,share,techlibs/common/blackbox.v)) $(eval $(call add_share_file,share,techlibs/common/pmux2mux.v)) $(eval $(call add_share_file,share,techlibs/common/adff2dff.v)) $(eval $(call add_share_file,share,techlibs/common/cells.lib)) diff --git a/techlibs/common/blackbox.sed b/techlibs/common/blackbox.sed deleted file mode 100644 index db8900344..000000000 --- a/techlibs/common/blackbox.sed +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sed -r -/^(wire|assign|reg|event|integer|localparam|\/\/|[\/ ]\*| *$|`)/ d; -/^(genvar|generate|always|initial|task|function)/,/^end/ d; -/^endmodule/ s/$/\n/; -s/ reg / /; diff --git a/techlibs/common/cellhelp.py b/techlibs/common/cellhelp.py new file mode 100644 index 000000000..5c44cb802 --- /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 000000000..9f8f6b313 --- /dev/null +++ b/techlibs/common/prep.cc @@ -0,0 +1,170 @@ +/* + * 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 + +struct PrepPass : public ScriptPass +{ + PrepPass() : ScriptPass("prep", "generic synthesis script") { } + + virtual void help() YS_OVERRIDE + { + // |---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(" -flatten\n"); + log(" flatten the design before synthesis. this will pass '-auto-top' to\n"); + log(" 'hierarchy' if no top module is specified.\n"); + log("\n"); + log(" -ifx\n"); + log(" passed to 'proc'. uses verilog simulation behavior for verilog if/case\n"); + log(" undef handling. this also prevents 'wreduce' from being run.\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"); + help_script(); + log("\n"); + } + + string top_module, fsm_opts, memory_opts; + bool flatten, ifxmode; + + virtual void clear_flags() YS_OVERRIDE + { + top_module.clear(); + memory_opts.clear(); + flatten = false; + ifxmode = false; + } + + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + 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] == "-flatten") { + flatten = true; + continue; + } + if (args[argidx] == "-ifx") { + ifxmode = true; + 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"); + + log_header(design, "Executing PREP pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + virtual void script() YS_OVERRIDE + { + + if (check_label("begin")) + { + if (help_mode) { + run("hierarchy -check [-top <top>]"); + } else { + if (top_module.empty()) { + if (flatten) + run("hierarchy -check -auto-top"); + else + run("hierarchy -check"); + } else + run(stringf("hierarchy -check -top %s", top_module.c_str())); + } + } + + if (check_label("coarse")) + { + run(ifxmode ? "proc -ifx" : "proc"); + if (help_mode || flatten) + run("flatten", "(if -flatten)"); + run("opt_expr -keepdc"); + run("opt_clean"); + run("check"); + run("opt -keepdc"); + if (!ifxmode) + run("wreduce"); + run("memory_dff" + (help_mode ? " [-nordff]" : memory_opts)); + run("opt_clean"); + run("memory_collect"); + run("opt -keepdc -fast"); + } + + if (check_label("check")) + { + run("stat"); + run("check"); + } + } +} PrepPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/common/simcells.v b/techlibs/common/simcells.v index eb62d7830..c4f170a3c 100644 --- a/techlibs/common/simcells.v +++ b/techlibs/common/simcells.v @@ -2,11 +2,11 @@ * 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 @@ -19,91 +19,400 @@ * * The internal logic cell simulation library. * - * This verilog library contains simple simulation models for the internal + * This Verilog library contains simple simulation models for the internal * logic cells ($_NOT_ , $_AND_ , ...) that are generated by the default technology * mapper (see "techmap.v" in this directory) and expected by the "abc" pass. * */ -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 -module \$_AOI3_ (A, B, C, Y); +// |---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; +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; +assign Y = U ? T ? (S ? H : G) : + (S ? F : E) : + T ? (S ? D : C) : + (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; +assign Y = V ? U ? T ? (S ? P : O) : + (S ? N : M) : + T ? (S ? L : K) : + (S ? J : I) : + U ? T ? (S ? H : G) : + (S ? F : E) : + T ? (S ? D : C) : + (S ? B : A); +endmodule + +// |---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 -module \$_SR_NN_ (S, R, Q); +// |---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 + +// |---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 @@ -114,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 @@ -125,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 @@ -136,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 @@ -147,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 @@ -155,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 @@ -163,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 @@ -171,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 @@ -179,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 @@ -187,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 @@ -195,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 @@ -206,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 @@ -217,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 @@ -228,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 @@ -239,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 @@ -250,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 @@ -261,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 @@ -272,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 @@ -283,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 @@ -296,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 @@ -309,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 @@ -322,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 @@ -335,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 @@ -348,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 @@ -361,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 @@ -374,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 @@ -387,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 @@ -396,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 @@ -405,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 @@ -418,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 @@ -431,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 @@ -444,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 @@ -457,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 @@ -470,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 @@ -483,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 @@ -496,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/simlib.v b/techlibs/common/simlib.v index d0feadd81..342555024 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -2,11 +2,11 @@ * 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 @@ -19,7 +19,7 @@ * * The Simulation Library. * - * This verilog library contains simple simulation models for the internal + * This Verilog library contains simple simulation models for the internal * cells ($not, ...) generated by the frontends and used in most passes. * * This library can be used to verify the internal netlists as generated @@ -33,6 +33,12 @@ // -------------------------------------------------------- +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $not (A, Y) +//- +//- A bit-wise inverter. This corresponds to the Verilog unary prefix '~' operator. +//- module \$not (A, Y); parameter A_SIGNED = 0; @@ -55,6 +61,12 @@ endmodule // -------------------------------------------------------- +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $pos (A, Y) +//- +//- A buffer. This corresponds to the Verilog unary prefix '+' operator. +//- module \$pos (A, Y); parameter A_SIGNED = 0; @@ -76,6 +88,12 @@ endmodule // -------------------------------------------------------- +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $neg (A, Y) +//- +//- An arithmetic inverter. This corresponds to the Verilog unary prefix '-' operator. +//- module \$neg (A, Y); parameter A_SIGNED = 0; @@ -97,6 +115,12 @@ endmodule // -------------------------------------------------------- +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $and (A, B, Y) +//- +//- A bit-wise AND. This corresponds to the Verilog '&' operator. +//- module \$and (A, B, Y); parameter A_SIGNED = 0; @@ -121,6 +145,12 @@ endmodule // -------------------------------------------------------- +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $or (A, B, Y) +//- +//- A bit-wise OR. This corresponds to the Verilog '|' operator. +//- module \$or (A, B, Y); parameter A_SIGNED = 0; @@ -145,6 +175,12 @@ endmodule // -------------------------------------------------------- +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $xor (A, B, Y) +//- +//- A bit-wise XOR. This corresponds to the Verilog '^' operator. +//- module \$xor (A, B, Y); parameter A_SIGNED = 0; @@ -169,6 +205,12 @@ endmodule // -------------------------------------------------------- +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $xnor (A, B, Y) +//- +//- A bit-wise XNOR. This corresponds to the Verilog '~^' operator. +//- module \$xnor (A, B, Y); parameter A_SIGNED = 0; @@ -193,6 +235,12 @@ endmodule // -------------------------------------------------------- +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $reduce_and (A, B, Y) +//- +//- An AND reduction. This corresponds to the Verilog unary prefix '&' operator. +//- module \$reduce_and (A, Y); parameter A_SIGNED = 0; @@ -214,6 +262,12 @@ endmodule // -------------------------------------------------------- +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $reduce_or (A, B, Y) +//- +//- An OR reduction. This corresponds to the Verilog unary prefix '|' operator. +//- module \$reduce_or (A, Y); parameter A_SIGNED = 0; @@ -235,6 +289,12 @@ endmodule // -------------------------------------------------------- +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $reduce_xor (A, B, Y) +//- +//- A XOR reduction. This corresponds to the Verilog unary prefix '^' operator. +//- module \$reduce_xor (A, Y); parameter A_SIGNED = 0; @@ -256,6 +316,12 @@ endmodule // -------------------------------------------------------- +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $reduce_xnor (A, B, Y) +//- +//- A XNOR reduction. This corresponds to the Verilog unary prefix '~^' operator. +//- module \$reduce_xnor (A, Y); parameter A_SIGNED = 0; @@ -277,6 +343,13 @@ endmodule // -------------------------------------------------------- +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $reduce_bool (A, B, Y) +//- +//- An OR reduction. This cell type is used instead of $reduce_or when a signal is +//- implicitly converted to a boolean signal, e.g. for operands of '&&' and '||'. +//- module \$reduce_bool (A, Y); parameter A_SIGNED = 0; @@ -1156,6 +1229,48 @@ endmodule `endif // -------------------------------------------------------- +module \$sop (A, Y); + +parameter WIDTH = 0; +parameter DEPTH = 0; +parameter TABLE = 0; + +input [WIDTH-1:0] A; +output reg Y; + +integer i, j; +reg match; + +always @* begin + Y = 0; + for (i = 0; i < DEPTH; i=i+1) begin + match = 1; + for (j = 0; j < WIDTH; j=j+1) begin + if (TABLE[2*WIDTH*i + 2*j + 0] && A[j]) match = 0; + if (TABLE[2*WIDTH*i + 2*j + 1] && !A[j]) match = 0; + end + if (match) Y = 1; + end +end + +endmodule + +// -------------------------------------------------------- + +module \$tribuf (A, EN, Y); + +parameter WIDTH = 0; + +input [WIDTH-1:0] A; +input EN; +output [WIDTH-1:0] Y; + +assign Y = EN ? A : 'bz; + +endmodule + +// -------------------------------------------------------- + module \$assert (A, EN); input A, EN; @@ -1163,7 +1278,24 @@ input A, EN; `ifndef SIMLIB_NOCHECKS always @* begin if (A !== 1'b1 && EN === 1'b1) begin - $display("Assertation failed!"); + $display("Assertion %m failed!"); + $stop; + end +end +`endif + +endmodule + +// -------------------------------------------------------- + +module \$assume (A, EN); + +input A, EN; + +`ifndef SIMLIB_NOCHECKS +always @* begin + if (A !== 1'b1 && EN === 1'b1) begin + $display("Assumption %m failed!"); $stop; end end @@ -1208,7 +1340,7 @@ wire [WIDTH-1:0] pos_clr = CLR_POLARITY ? CLR : ~CLR; genvar i; generate - for (i = 0; i < WIDTH; i = i+1) begin:bit + for (i = 0; i < WIDTH; i = i+1) begin:bitslices always @(posedge pos_set[i], posedge pos_clr[i]) if (pos_clr[i]) Q[i] <= 0; @@ -1277,7 +1409,7 @@ wire [WIDTH-1:0] pos_clr = CLR_POLARITY ? CLR : ~CLR; genvar i; generate - for (i = 0; i < WIDTH; i = i+1) begin:bit + for (i = 0; i < WIDTH; i = i+1) begin:bitslices always @(posedge pos_set[i], posedge pos_clr[i], posedge pos_clk) if (pos_clr[i]) Q[i] <= 0; @@ -1328,7 +1460,7 @@ output reg [WIDTH-1:0] Q; always @* begin if (EN == EN_POLARITY) - Q <= D; + Q = D; end endmodule @@ -1353,14 +1485,14 @@ wire [WIDTH-1:0] pos_clr = CLR_POLARITY ? CLR : ~CLR; genvar i; generate - for (i = 0; i < WIDTH; i = i+1) begin:bit + for (i = 0; i < WIDTH; i = i+1) begin:bitslices always @* if (pos_clr[i]) - Q[i] <= 0; + Q[i] = 0; else if (pos_set[i]) - Q[i] <= 1; + Q[i] = 1; else if (pos_en) - Q[i] <= D[i]; + Q[i] = D[i]; end endgenerate @@ -1463,7 +1595,7 @@ endmodule // -------------------------------------------------------- `ifndef SIMLIB_NOMEM -module \$memrd (CLK, ADDR, DATA); +module \$memrd (CLK, EN, ADDR, DATA); parameter MEMID = ""; parameter ABITS = 8; @@ -1473,7 +1605,7 @@ parameter CLK_ENABLE = 0; parameter CLK_POLARITY = 0; parameter TRANSPARENT = 0; -input CLK; +input CLK, EN; input [ABITS-1:0] ADDR; output [WIDTH-1:0] DATA; @@ -1514,24 +1646,49 @@ endmodule // -------------------------------------------------------- -module \$mem (RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA); +module \$meminit (ADDR, DATA); parameter MEMID = ""; -parameter SIZE = 256; -parameter OFFSET = 0; parameter ABITS = 8; parameter WIDTH = 8; +parameter WORDS = 1; + +parameter PRIORITY = 0; + +input [ABITS-1:0] ADDR; +input [WORDS*WIDTH-1:0] DATA; -parameter RD_PORTS = 1; +initial begin + if (MEMID != "") begin + $display("ERROR: Found non-simulatable instance of $meminit!"); + $finish; + end +end + +endmodule + +// -------------------------------------------------------- + +module \$mem (RD_CLK, RD_EN, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA); + +parameter MEMID = ""; +parameter signed SIZE = 4; +parameter signed OFFSET = 0; +parameter signed ABITS = 2; +parameter signed WIDTH = 8; +parameter signed INIT = 1'bx; + +parameter signed RD_PORTS = 1; parameter RD_CLK_ENABLE = 1'b1; parameter RD_CLK_POLARITY = 1'b1; parameter RD_TRANSPARENT = 1'b1; -parameter WR_PORTS = 1; +parameter signed WR_PORTS = 1; parameter WR_CLK_ENABLE = 1'b1; parameter WR_CLK_POLARITY = 1'b1; input [RD_PORTS-1:0] RD_CLK; +input [RD_PORTS-1:0] RD_EN; input [RD_PORTS*ABITS-1:0] RD_ADDR; output reg [RD_PORTS*WIDTH-1:0] RD_DATA; @@ -1561,25 +1718,36 @@ function port_active; end endfunction +initial begin + for (i = 0; i < SIZE; i = i+1) + memory[i] = INIT >>> (i*WIDTH); +end + always @(RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA) begin `ifdef SIMLIB_MEMDELAY #`SIMLIB_MEMDELAY; `endif for (i = 0; i < RD_PORTS; i = i+1) begin - if ((!RD_TRANSPARENT[i] && RD_CLK_ENABLE[i]) && port_active(RD_CLK_ENABLE[i], RD_CLK_POLARITY[i], LAST_RD_CLK[i], RD_CLK[i])) + if (!RD_TRANSPARENT[i] && RD_CLK_ENABLE[i] && RD_EN[i] && port_active(RD_CLK_ENABLE[i], RD_CLK_POLARITY[i], LAST_RD_CLK[i], RD_CLK[i])) begin + // $display("Read from %s: addr=%b data=%b", MEMID, RD_ADDR[i*ABITS +: ABITS], memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET]); RD_DATA[i*WIDTH +: WIDTH] <= memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET]; + end end for (i = 0; i < WR_PORTS; i = i+1) begin if (port_active(WR_CLK_ENABLE[i], WR_CLK_POLARITY[i], LAST_WR_CLK[i], WR_CLK[i])) for (j = 0; j < WIDTH; j = j+1) - if (WR_EN[i*WIDTH+j]) + if (WR_EN[i*WIDTH+j]) begin + // $display("Write to %s: addr=%b data=%b", MEMID, WR_ADDR[i*ABITS +: ABITS], WR_DATA[i*WIDTH+j]); memory[WR_ADDR[i*ABITS +: ABITS] - OFFSET][j] = WR_DATA[i*WIDTH+j]; + end end for (i = 0; i < RD_PORTS; i = i+1) begin - if ((RD_TRANSPARENT[i] || !RD_CLK_ENABLE[i]) && port_active(RD_CLK_ENABLE[i], RD_CLK_POLARITY[i], LAST_RD_CLK[i], RD_CLK[i])) + if ((RD_TRANSPARENT[i] || !RD_CLK_ENABLE[i]) && port_active(RD_CLK_ENABLE[i], RD_CLK_POLARITY[i], LAST_RD_CLK[i], RD_CLK[i])) begin + // $display("Transparent read from %s: addr=%b data=%b", MEMID, RD_ADDR[i*ABITS +: ABITS], memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET]); RD_DATA[i*WIDTH +: WIDTH] <= memory[RD_ADDR[i*ABITS +: ABITS] - OFFSET]; + end end LAST_RD_CLK <= RD_CLK; diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index 69ef5bc55..859a6606f 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -2,11 +2,11 @@ * 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 @@ -25,22 +25,11 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool check_label(bool &active, std::string run_from, std::string run_to, std::string label) +struct SynthPass : public ScriptPass { - 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; -} + SynthPass() : ScriptPass("synth", "generic synthesis script") { } -struct SynthPass : public Pass { - SynthPass() : Pass("synth", "generic synthesis script") { } - virtual void help() + virtual void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -52,12 +41,26 @@ struct SynthPass : public Pass { log(" -top <module>\n"); log(" use the specified module as top module (default='top')\n"); log("\n"); + log(" -flatten\n"); + log(" flatten the design before synthesis. this will pass '-auto-top' to\n"); + log(" 'hierarchy' if no top module is specified.\n"); + log("\n"); log(" -encfile <file>\n"); log(" passed to 'fsm_recode' via 'fsm'\n"); log("\n"); + log(" -nofsm\n"); + log(" do not run FSM optimization\n"); + log("\n"); log(" -noabc\n"); log(" do not run abc (as if yosys was compiled without ABC support)\n"); log("\n"); + log(" -noalumacc\n"); + log(" do not run 'alumacc' pass. i.e. keep arithmetic operators in\n"); + log(" their direct form ($add, $sub, etc.).\n"); + log("\n"); + log(" -nordff\n"); + log(" passed to 'memory'. 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"); @@ -65,41 +68,29 @@ struct SynthPass : public Pass { 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(" coarse:\n"); - log(" proc\n"); - log(" opt\n"); - log(" wreduce\n"); - log(" alumacc\n"); - log(" share\n"); - log(" opt\n"); - log(" fsm\n"); - log(" opt -fast\n"); - log(" memory -nomap\n"); - log(" opt_clean\n"); - log("\n"); - log(" fine:\n"); - log(" opt -fast -full\n"); - log(" memory_map\n"); - log(" opt -full\n"); - log(" techmap\n"); - log(" opt -fast\n"); - #ifdef YOSYS_ENABLE_ABC - log("\n"); - log(" abc:\n"); - log(" abc -fast\n"); - log(" opt -fast\n"); - #endif + help_script(); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + + string top_module, fsm_opts, memory_opts; + bool flatten, noalumacc, nofsm, noabc; + + virtual void clear_flags() YS_OVERRIDE { - std::string top_module, fsm_opts; - std::string run_from, run_to; - bool noabc = false; + top_module.clear(); + fsm_opts.clear(); + memory_opts.clear(); + + flatten = false; + noalumacc = false; + nofsm = false; + noabc = false; + } + + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + string run_from, run_to; + clear_flags(); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -123,10 +114,26 @@ struct SynthPass : public Pass { } continue; } + if (args[argidx] == "-flatten") { + flatten = true; + continue; + } + if (args[argidx] == "-nofsm") { + nofsm = true; + continue; + } if (args[argidx] == "-noabc") { noabc = true; continue; } + if (args[argidx] == "-noalumacc") { + noalumacc = true; + continue; + } + if (args[argidx] == "-nordff") { + memory_opts += " -nordff"; + continue; + } break; } extra_args(args, argidx, design); @@ -134,52 +141,75 @@ struct SynthPass : public Pass { if (!design->full_selection()) log_cmd_error("This comannd only operates on fully selected designs!\n"); - bool active = run_from.empty(); - - log_header("Executing SYNTH pass.\n"); + log_header(design, "Executing SYNTH pass.\n"); log_push(); - if (check_label(active, run_from, run_to, "begin")) + run_script(design, run_from, run_to); + + log_pop(); + } + + virtual void script() YS_OVERRIDE + { + if (check_label("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 (help_mode) { + run("hierarchy -check [-top <top>]"); + } else { + if (top_module.empty()) { + if (flatten) + run("hierarchy -check -auto-top"); + else + run("hierarchy -check"); + } else + run(stringf("hierarchy -check -top %s", top_module.c_str())); + } } - if (check_label(active, run_from, run_to, "coarse")) + if (check_label("coarse")) { - Pass::call(design, "proc"); - Pass::call(design, "opt"); - Pass::call(design, "wreduce"); - Pass::call(design, "alumacc"); - Pass::call(design, "share"); - Pass::call(design, "opt"); - Pass::call(design, "fsm" + fsm_opts); - Pass::call(design, "opt -fast"); - Pass::call(design, "memory -nomap"); - Pass::call(design, "opt_clean"); + run("proc"); + if (help_mode || flatten) + run("flatten", "(if -flatten)"); + run("opt_expr"); + run("opt_clean"); + run("check"); + run("opt"); + run("wreduce"); + if (!noalumacc) + run("alumacc"); + run("share"); + run("opt"); + if (!nofsm) + run("fsm" + fsm_opts); + run("opt -fast"); + run("memory -nomap" + memory_opts); + run("opt_clean"); } - if (check_label(active, run_from, run_to, "fine")) + if (check_label("fine")) { - Pass::call(design, "opt -fast -full"); - Pass::call(design, "memory_map"); - Pass::call(design, "opt -full"); - Pass::call(design, "techmap"); - Pass::call(design, "opt -fast"); + run("opt -fast -full"); + run("memory_map"); + run("opt -full"); + run("techmap"); + run("opt -fast"); + + if (!noabc) { + #ifdef YOSYS_ENABLE_ABC + run("abc -fast"); + run("opt -fast"); + #endif + } } - #ifdef YOSYS_ENABLE_ABC - if (check_label(active, run_from, run_to, "abc") && !noabc) + if (check_label("check")) { - Pass::call(design, "abc -fast"); - Pass::call(design, "opt -fast"); + run("hierarchy -check"); + run("stat"); + run("check"); } - #endif - - log_pop(); } } SynthPass; - + PRIVATE_NAMESPACE_END diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v index e0ecf0c48..90c4ed7eb 100644 --- a/techlibs/common/techmap.v +++ b/techlibs/common/techmap.v @@ -2,11 +2,11 @@ * 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 @@ -19,8 +19,8 @@ * * The internal logic cell technology mapper. * - * This verilog library contains the mapping of internal cells (e.g. $not with - * variable bit width) to the internal logic cells (such as the single bit $_NOT_ + * This Verilog library contains the mapping of internal cells (e.g. $not with + * variable bit width) to the internal logic cells (such as the single bit $_NOT_ * gate). Usually this logic network is then mapped to the actual technology * using e.g. the "abc" pass. * @@ -59,7 +59,7 @@ module _90_simplemap_compare_ops; endmodule (* techmap_simplemap *) -(* techmap_celltype = "$pos $slice $concat $mux" *) +(* techmap_celltype = "$pos $slice $concat $mux $tribuf" *) module _90_simplemap_various; endmodule @@ -93,7 +93,7 @@ module _90_shift_ops_shr_shl_sshl_sshr (A, B, Y); localparam BB_WIDTH = `MIN($clog2(shift_left ? Y_WIDTH : A_SIGNED ? WIDTH : A_WIDTH) + 1, B_WIDTH); wire [1023:0] _TECHMAP_DO_00_ = "proc;;"; - wire [1023:0] _TECHMAP_DO_01_ = "RECURSION; CONSTMAP; opt_muxtree; opt_const -mux_undef -mux_bool -fine;;;"; + wire [1023:0] _TECHMAP_DO_01_ = "RECURSION; CONSTMAP; opt_muxtree; opt_expr -mux_undef -mux_bool -fine;;;"; integer i; reg [WIDTH-1:0] buffer; @@ -136,7 +136,7 @@ module _90_shift_shiftx (A, B, Y); localparam extbit = _TECHMAP_CELLTYPE_ == "$shift" ? 1'b0 : 1'bx; wire [1023:0] _TECHMAP_DO_00_ = "proc;;"; - wire [1023:0] _TECHMAP_DO_01_ = "CONSTMAP; opt_muxtree; opt_const -mux_undef -mux_bool -fine;;;"; + wire [1023:0] _TECHMAP_DO_01_ = "CONSTMAP; opt_muxtree; opt_expr -mux_undef -mux_bool -fine;;;"; integer i; reg [WIDTH-1:0] buffer; @@ -451,15 +451,9 @@ endmodule // -------------------------------------------------------- `ifndef NOLUT -(* techmap_celltype = "$lut" *) -module _90_lut (A, Y); - parameter WIDTH = 1; - parameter LUT = 0; - - input [WIDTH-1:0] A; - output Y; - - assign Y = LUT[A]; +(* techmap_simplemap *) +(* techmap_celltype = "$lut $sop" *) +module _90_lut; endmodule `endif diff --git a/techlibs/greenpak4/Makefile.inc b/techlibs/greenpak4/Makefile.inc new file mode 100644 index 000000000..969b7c80d --- /dev/null +++ b/techlibs/greenpak4/Makefile.inc @@ -0,0 +1,7 @@ + +OBJS += techlibs/greenpak4/synth_greenpak4.o +OBJS += techlibs/greenpak4/greenpak4_counters.o + +$(eval $(call add_share_file,share/greenpak4,techlibs/greenpak4/cells_map.v)) +$(eval $(call add_share_file,share/greenpak4,techlibs/greenpak4/cells_sim.v)) +$(eval $(call add_share_file,share/greenpak4,techlibs/greenpak4/gp_dff.lib)) diff --git a/techlibs/greenpak4/cells_map.v b/techlibs/greenpak4/cells_map.v new file mode 100644 index 000000000..b7d750ae0 --- /dev/null +++ b/techlibs/greenpak4/cells_map.v @@ -0,0 +1,68 @@ +module GP_DFFS(input D, CLK, nSET, output reg Q); + parameter [0:0] INIT = 1'bx; + GP_DFFSR #( + .INIT(INIT), + .SRMODE(1'b1), + ) _TECHMAP_REPLACE_ ( + .D(D), + .CLK(CLK), + .nSR(nSET), + .Q(Q) + ); +endmodule + +module GP_DFFR(input D, CLK, nRST, output reg Q); + parameter [0:0] INIT = 1'bx; + GP_DFFSR #( + .INIT(INIT), + .SRMODE(1'b0), + ) _TECHMAP_REPLACE_ ( + .D(D), + .CLK(CLK), + .nSR(nRST), + .Q(Q) + ); +endmodule + +module GP_OBUFT(input IN, input OE, output OUT); + GP_IOBUF _TECHMAP_REPLACE_ ( + .IN(IN), + .OE(OE), + .IO(OUT), + .OUT() + ); +endmodule + +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + + input [WIDTH-1:0] A; + output Y; + + generate + if (WIDTH == 1) begin + if(LUT == 2'b01) begin + GP_INV _TECHMAP_REPLACE_ (.OUT(Y), .IN(A[0]) ); + end + else begin + GP_2LUT #(.INIT({2'b00, LUT})) _TECHMAP_REPLACE_ (.OUT(Y), + .IN0(A[0]), .IN1(1'b0)); + end + end else + if (WIDTH == 2) begin + GP_2LUT #(.INIT(LUT)) _TECHMAP_REPLACE_ (.OUT(Y), + .IN0(A[0]), .IN1(A[1])); + end else + if (WIDTH == 3) begin + GP_3LUT #(.INIT(LUT)) _TECHMAP_REPLACE_ (.OUT(Y), + .IN0(A[0]), .IN1(A[1]), .IN2(A[2])); + end else + if (WIDTH == 4) begin + GP_4LUT #(.INIT(LUT)) _TECHMAP_REPLACE_ (.OUT(Y), + .IN0(A[0]), .IN1(A[1]), .IN2(A[2]), .IN3(A[3])); + end else begin + wire _TECHMAP_FAIL_ = 1; + end + endgenerate +endmodule diff --git a/techlibs/greenpak4/cells_sim.v b/techlibs/greenpak4/cells_sim.v new file mode 100644 index 000000000..be8e66c66 --- /dev/null +++ b/techlibs/greenpak4/cells_sim.v @@ -0,0 +1,354 @@ +`timescale 1ns/1ps + +module GP_2LUT(input IN0, IN1, output OUT); + parameter [3:0] INIT = 0; + assign OUT = INIT[{IN1, IN0}]; +endmodule + +module GP_3LUT(input IN0, IN1, IN2, output OUT); + parameter [7:0] INIT = 0; + assign OUT = INIT[{IN2, IN1, IN0}]; +endmodule + +module GP_4LUT(input IN0, IN1, IN2, IN3, output OUT); + parameter [15:0] INIT = 0; + assign OUT = INIT[{IN3, IN2, IN1, IN0}]; +endmodule + +module GP_ABUF(input wire IN, output wire OUT); + + assign OUT = IN; + + //cannot simulate mixed signal IP + +endmodule + +module GP_ACMP(input wire PWREN, input wire VIN, input wire VREF, output reg OUT); + + parameter BANDWIDTH = "HIGH"; + parameter VIN_ATTEN = 1; + parameter VIN_ISRC_EN = 0; + parameter HYSTERESIS = 0; + + initial OUT = 0; + + //cannot simulate mixed signal IP + +endmodule + +module GP_BANDGAP(output reg OK, output reg VOUT); + parameter AUTO_PWRDN = 1; + parameter CHOPPER_EN = 1; + parameter OUT_DELAY = 100; + + //cannot simulate mixed signal IP + +endmodule + +module GP_COUNT8(input CLK, input wire RST, output reg OUT); + + parameter RESET_MODE = "RISING"; + + parameter COUNT_TO = 8'h1; + parameter CLKIN_DIVIDE = 1; + + //more complex hard IP blocks are not supported for simulation yet + + reg[7:0] count = COUNT_TO; + + //Combinatorially output whenever we wrap low + always @(*) begin + OUT <= (count == 8'h0); + end + + //POR or SYSRST reset value is COUNT_TO. Datasheet is unclear but conversations w/ Silego confirm. + //Runtime reset value is clearly 0 except in count/FSM cells where it's configurable but we leave at 0 for now. + //Datasheet seems to indicate that reset is asynchronous, but for now we model as sync due to Yosys issues... + always @(posedge CLK) begin + + count <= count - 1'd1; + + if(count == 0) + count <= COUNT_TO; + + /* + if((RESET_MODE == "RISING") && RST) + count <= 0; + if((RESET_MODE == "FALLING") && !RST) + count <= 0; + if((RESET_MODE == "BOTH") && RST) + count <= 0; + */ + end + +endmodule + +module GP_COUNT14(input CLK, input wire RST, output reg OUT); + + parameter RESET_MODE = "RISING"; + + parameter COUNT_TO = 14'h1; + parameter CLKIN_DIVIDE = 1; + + //more complex hard IP blocks are not supported for simulation yet + +endmodule + +module GP_DELAY(input IN, output reg OUT); + + parameter DELAY_STEPS = 1; + + //TODO: additional delay/glitch filter mode + + initial OUT = 0; + + generate + + //TODO: These delays are PTV dependent! For now, hard code 3v3 timing + //Change simulation-mode delay depending on global Vdd range (how to specify this?) + always @(*) begin + case(DELAY_STEPS) + 1: #166 OUT = IN; + 2: #318 OUT = IN; + 2: #471 OUT = IN; + 3: #622 OUT = IN; + default: begin + $display("ERROR: GP_DELAY must have DELAY_STEPS in range [1,4]"); + $finish; + end + endcase + end + + endgenerate + +endmodule + +module GP_DFF(input D, CLK, output reg Q); + parameter [0:0] INIT = 1'bx; + initial Q = INIT; + always @(posedge CLK) begin + Q <= D; + end +endmodule + +module GP_DFFR(input D, CLK, nRST, output reg Q); + parameter [0:0] INIT = 1'bx; + initial Q = INIT; + always @(posedge CLK, negedge nRST) begin + if (!nRST) + Q <= 1'b0; + else + Q <= D; + end +endmodule + +module GP_DFFS(input D, CLK, nSET, output reg Q); + parameter [0:0] INIT = 1'bx; + initial Q = INIT; + always @(posedge CLK, negedge nSET) begin + if (!nSET) + Q <= 1'b1; + else + Q <= D; + end +endmodule + +module GP_DFFSR(input D, CLK, nSR, output reg Q); + parameter [0:0] INIT = 1'bx; + parameter [0:0] SRMODE = 1'bx; + initial Q = INIT; + always @(posedge CLK, negedge nSR) begin + if (!nSR) + Q <= SRMODE; + else + Q <= D; + end +endmodule + +module GP_IBUF(input IN, output OUT); + assign OUT = IN; +endmodule + +module GP_IOBUF(input IN, input OE, output OUT, inout IO); + assign OUT = IO; + assign IO = OE ? IN : 1'bz; +endmodule + +module GP_INV(input IN, output OUT); + assign OUT = ~IN; +endmodule + +module GP_LFOSC(input PWRDN, output reg CLKOUT); + + parameter PWRDN_EN = 0; + parameter AUTO_PWRDN = 0; + parameter OUT_DIV = 1; + + initial CLKOUT = 0; + + //auto powerdown not implemented for simulation + //output dividers not implemented for simulation + + always begin + if(PWRDN) + CLKOUT = 0; + else begin + //half period of 1730 Hz + #289017; + CLKOUT = ~CLKOUT; + end + end + +endmodule + +module GP_OBUF(input IN, output OUT); + assign OUT = IN; +endmodule + +module GP_OBUFT(input IN, input OE, output OUT); + assign OUT = OE ? IN : 1'bz; +endmodule + +module GP_PGA(input wire VIN_P, input wire VIN_N, input wire VIN_SEL, output reg VOUT); + + parameter GAIN = 1; + parameter INPUT_MODE = "SINGLE"; + + initial VOUT = 0; + + //cannot simulate mixed signal IP + +endmodule + +module GP_POR(output reg RST_DONE); + parameter POR_TIME = 500; + + initial begin + RST_DONE = 0; + + if(POR_TIME == 4) + #4000; + else if(POR_TIME == 500) + #500000; + else begin + $display("ERROR: bad POR_TIME for GP_POR cell"); + $finish; + end + + RST_DONE = 1; + + end + +endmodule + +module GP_RCOSC(input PWRDN, output reg CLKOUT_PREDIV, output reg CLKOUT_FABRIC); + + parameter PWRDN_EN = 0; + parameter AUTO_PWRDN = 0; + parameter PRE_DIV = 1; + parameter FABRIC_DIV = 1; + parameter OSC_FREQ = "25k"; + + initial CLKOUT_PREDIV = 0; + initial CLKOUT_FABRIC = 0; + + //output dividers not implemented for simulation + //auto powerdown not implemented for simulation + + always begin + if(PWRDN) begin + CLKOUT_PREDIV = 0; + CLKOUT_FABRIC = 0; + end + else begin + + if(OSC_FREQ == "25k") begin + //half period of 25 kHz + #20000; + end + + else begin + //half period of 2 MHz + #250; + end + + CLKOUT_PREDIV = ~CLKOUT_PREDIV; + CLKOUT_FABRIC = ~CLKOUT_FABRIC; + end + end + +endmodule + +module GP_RINGOSC(input PWRDN, output reg CLKOUT_PREDIV, output reg CLKOUT_FABRIC); + + parameter PWRDN_EN = 0; + parameter AUTO_PWRDN = 0; + parameter PRE_DIV = 1; + parameter FABRIC_DIV = 1; + + initial CLKOUT_PREDIV = 0; + initial CLKOUT_FABRIC = 0; + + //output dividers not implemented for simulation + //auto powerdown not implemented for simulation + + always begin + if(PWRDN) begin + CLKOUT_PREDIV = 0; + CLKOUT_FABRIC = 0; + end + else begin + //half period of 27 MHz + #18.518; + CLKOUT_PREDIV = ~CLKOUT_PREDIV; + CLKOUT_FABRIC = ~CLKOUT_FABRIC; + end + end + +endmodule + +module GP_SHREG(input nRST, input CLK, input IN, output OUTA, output OUTB); + + parameter OUTA_TAP = 1; + parameter OUTA_INVERT = 0; + parameter OUTB_TAP = 1; + + reg[15:0] shreg = 0; + + always @(posedge CLK, negedge nRST) begin + + if(!nRST) + shreg = 0; + + else + shreg <= {shreg[14:0], IN}; + + end + + assign OUTA = (OUTA_INVERT) ? ~shreg[OUTA_TAP - 1] : shreg[OUTA_TAP - 1]; + assign OUTB = shreg[OUTB_TAP - 1]; + +endmodule + +//keep constraint needed to prevent optimization since we have no outputs +(* keep *) +module GP_SYSRESET(input RST); + parameter RESET_MODE = "RISING"; + + //cannot simulate whole system reset + +endmodule + +module GP_VDD(output OUT); + assign OUT = 1; +endmodule + +module GP_VREF(input VIN, output reg VOUT); + parameter VIN_DIV = 1; + parameter VREF = 0; + //cannot simulate mixed signal IP +endmodule + +module GP_VSS(output OUT); + assign OUT = 0; +endmodule diff --git a/techlibs/greenpak4/gp_dff.lib b/techlibs/greenpak4/gp_dff.lib new file mode 100644 index 000000000..b4b8c1028 --- /dev/null +++ b/techlibs/greenpak4/gp_dff.lib @@ -0,0 +1,36 @@ +library(gp_dff) { + cell(GP_DFF) { + area: 1; + ff("IQ", "IQN") { clocked_on: CLK; + next_state: D; } + pin(CLK) { direction: input; + clock: true; } + pin(D) { direction: input; } + pin(Q) { direction: output; + function: "IQ"; } + } + cell(GP_DFFS) { + area: 1; + ff("IQ", "IQN") { clocked_on: CLK; + next_state: D; + preset: "nSET'"; } + pin(CLK) { direction: input; + clock: true; } + pin(D) { direction: input; } + pin(Q) { direction: output; + function: "IQ"; } + pin(nSET) { direction: input; } + } + cell(GP_DFFR) { + area: 1; + ff("IQ", "IQN") { clocked_on: CLK; + next_state: D; + clear: "nRST'"; } + pin(CLK) { direction: input; + clock: true; } + pin(D) { direction: input; } + pin(Q) { direction: output; + function: "IQ"; } + pin(nRST) { direction: input; } + } +} diff --git a/techlibs/greenpak4/greenpak4_counters.cc b/techlibs/greenpak4/greenpak4_counters.cc new file mode 100644 index 000000000..998bb73bd --- /dev/null +++ b/techlibs/greenpak4/greenpak4_counters.cc @@ -0,0 +1,442 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2016 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" +#include "kernel/modtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +//get the list of cells hooked up to at least one bit of a given net +pool<Cell*> get_other_cells(const RTLIL::SigSpec& port, ModIndex& index, Cell* src) +{ + pool<Cell*> rval; + for(auto b : port) + { + pool<ModIndex::PortInfo> ports = index.query_ports(b); + for(auto x : ports) + { + if(x.cell == src) + continue; + rval.insert(x.cell); + } + } + return rval; +} + +//return true if there is a full-width bus connection from cell a port ap to cell b port bp +//if other_conns_allowed is false, then we require a strict point to point connection (no other links) +bool is_full_bus( + const RTLIL::SigSpec& sig, + ModIndex& index, + Cell* a, + RTLIL::IdString ap, + Cell* b, + RTLIL::IdString bp, + bool other_conns_allowed = false) +{ + for(auto s : sig) + { + pool<ModIndex::PortInfo> ports = index.query_ports(s); + bool found_a = false; + bool found_b = false; + for(auto x : ports) + { + if( (x.cell == a) && (x.port == ap) ) + found_a = true; + else if( (x.cell == b) && (x.port == bp) ) + found_b = true; + else if(!other_conns_allowed) + return false; + } + + if( (!found_a) || (!found_b) ) + return false; + } + + return true; +} + +//return true if the signal connects to one port only (nothing on the other end) +bool is_unconnected(const RTLIL::SigSpec& port, ModIndex& index) +{ + for(auto b : port) + { + pool<ModIndex::PortInfo> ports = index.query_ports(b); + if(ports.size() > 1) + return false; + } + + return true; +} + +struct CounterExtraction +{ + int width; //counter width + RTLIL::Wire* rwire; //the register output + bool has_reset; //true if we have a reset + RTLIL::SigSpec rst; //reset pin + int count_value; //value we count from + RTLIL::SigSpec clk; //clock signal + RTLIL::SigSpec outsig; //counter output signal + RTLIL::Cell* count_mux; //counter mux + RTLIL::Cell* count_reg; //counter register + RTLIL::Cell* underflow_inv; //inverter reduction for output-underflow detect +}; + +//attempt to extract a counter centered on the given cell +int greenpak4_counters_tryextract(ModIndex& index, Cell *cell, CounterExtraction& extract) +{ + SigMap& sigmap = index.sigmap; + + //GreenPak does not support counters larger than 14 bits so immediately skip anything bigger + int a_width = cell->getParam("\\A_WIDTH").as_int(); + extract.width = a_width; + if(a_width > 14) + return 1; + + //Second input must be a single bit + int b_width = cell->getParam("\\B_WIDTH").as_int(); + if(b_width != 1) + return 2; + + //Both inputs must be unsigned, so don't extract anything with a signed input + bool a_sign = cell->getParam("\\A_SIGNED").as_bool(); + bool b_sign = cell->getParam("\\B_SIGNED").as_bool(); + if(a_sign || b_sign) + return 3; + + //To be a counter, one input of the ALU must be a constant 1 + //TODO: can A or B be swapped in synthesized RTL or is B always the 1? + const RTLIL::SigSpec b_port = sigmap(cell->getPort("\\B")); + if(!b_port.is_fully_const() || (b_port.as_int() != 1) ) + return 4; + + //BI and CI must be constant 1 as well + const RTLIL::SigSpec bi_port = sigmap(cell->getPort("\\BI")); + if(!bi_port.is_fully_const() || (bi_port.as_int() != 1) ) + return 5; + const RTLIL::SigSpec ci_port = sigmap(cell->getPort("\\CI")); + if(!ci_port.is_fully_const() || (ci_port.as_int() != 1) ) + return 6; + + //CO and X must be unconnected (exactly one connection to each port) + if(!is_unconnected(sigmap(cell->getPort("\\CO")), index)) + return 7; + if(!is_unconnected(sigmap(cell->getPort("\\X")), index)) + return 8; + + //Y must have exactly one connection, and it has to be a $mux cell. + //We must have a direct bus connection from our Y to their A. + const RTLIL::SigSpec aluy = sigmap(cell->getPort("\\Y")); + pool<Cell*> y_loads = get_other_cells(aluy, index, cell); + if(y_loads.size() != 1) + return 9; + Cell* count_mux = *y_loads.begin(); + extract.count_mux = count_mux; + if(count_mux->type != "$mux") + return 10; + if(!is_full_bus(aluy, index, cell, "\\Y", count_mux, "\\A")) + return 11; + + //B connection of the mux is our underflow value + const RTLIL::SigSpec underflow = sigmap(count_mux->getPort("\\B")); + if(!underflow.is_fully_const()) + return 12; + extract.count_value = underflow.as_int(); + + //S connection of the mux must come from an inverter (need not be the only load) + const RTLIL::SigSpec muxsel = sigmap(count_mux->getPort("\\S")); + extract.outsig = muxsel; + pool<Cell*> muxsel_conns = get_other_cells(muxsel, index, count_mux); + Cell* underflow_inv = NULL; + for(auto c : muxsel_conns) + { + if(c->type != "$logic_not") + continue; + if(!is_full_bus(muxsel, index, c, "\\Y", count_mux, "\\S", true)) + continue; + + underflow_inv = c; + break; + } + if(underflow_inv == NULL) + return 13; + extract.underflow_inv = underflow_inv; + + //Y connection of the mux must have exactly one load, the counter's internal register + const RTLIL::SigSpec muxy = sigmap(count_mux->getPort("\\Y")); + pool<Cell*> muxy_loads = get_other_cells(muxy, index, count_mux); + if(muxy_loads.size() != 1) + return 14; + Cell* count_reg = *muxy_loads.begin(); + extract.count_reg = count_reg; + if(count_reg->type == "$dff") + extract.has_reset = false; + else if(count_reg->type == "$adff") + { + extract.has_reset = true; + + //Verify ARST_VALUE is zero and ARST_POLARITY is 1 + //TODO: infer an inverter to make it 1 if necessary, so we can support negative level resets? + if(count_reg->getParam("\\ARST_POLARITY").as_int() != 1) + return 22; + if(count_reg->getParam("\\ARST_VALUE").as_int() != 0) + return 23; + + //Save the reset + extract.rst = sigmap(count_reg->getPort("\\ARST")); + } + //TODO: support synchronous reset + else + return 15; + if(!is_full_bus(muxy, index, count_mux, "\\Y", count_reg, "\\D")) + return 16; + + //TODO: Verify count_reg CLK_POLARITY is 1 + + //Register output must have exactly two loads, the inverter and ALU + const RTLIL::SigSpec cnout = sigmap(count_reg->getPort("\\Q")); + pool<Cell*> cnout_loads = get_other_cells(cnout, index, count_reg); + if(cnout_loads.size() != 2) + return 17; + if(!is_full_bus(cnout, index, count_reg, "\\Q", underflow_inv, "\\A", true)) + return 18; + if(!is_full_bus(cnout, index, count_reg, "\\Q", cell, "\\A", true)) + return 19; + + //Look up the clock from the register + extract.clk = sigmap(count_reg->getPort("\\CLK")); + + //Register output net must have an INIT attribute equal to the count value + extract.rwire = cnout.as_wire(); + if(extract.rwire->attributes.find("\\init") == extract.rwire->attributes.end()) + return 20; + int rinit = extract.rwire->attributes["\\init"].as_int(); + if(rinit != extract.count_value) + return 21; + + return 0; +} + +void greenpak4_counters_worker( + ModIndex& index, + Cell *cell, + unsigned int& total_counters, + pool<Cell*>& cells_to_remove) +{ + SigMap& sigmap = index.sigmap; + + //Core of the counter must be an ALU + if (cell->type != "$alu") + return; + + //A input is the count value. Check if it has COUNT_EXTRACT set. + //If it's not a wire, don't even try + auto port = sigmap(cell->getPort("\\A")); + if(!port.is_wire()) + return; + RTLIL::Wire* a_wire = port.as_wire(); + bool force_extract = false; + bool never_extract = false; + string count_reg_src = a_wire->attributes["\\src"].decode_string().c_str(); + if(a_wire->attributes.find("\\COUNT_EXTRACT") != a_wire->attributes.end()) + { + pool<string> sa = a_wire->get_strpool_attribute("\\COUNT_EXTRACT"); + string extract_value; + if(sa.size() >= 1) + { + extract_value = *sa.begin(); + log(" Signal %s declared at %s has COUNT_EXTRACT = %s\n", + log_id(a_wire), + count_reg_src.c_str(), + extract_value.c_str()); + + if(extract_value == "FORCE") + force_extract = true; + else if(extract_value == "NO") + never_extract = true; + else if(extract_value == "AUTO") + {} //default + else + log_error(" Illegal COUNT_EXTRACT value %s (must be one of FORCE, NO, AUTO)\n", + extract_value.c_str()); + } + } + + //If we're explicitly told not to extract, don't infer a counter + if(never_extract) + return; + + //Attempt to extract a counter + CounterExtraction extract; + int reason = greenpak4_counters_tryextract(index, cell, extract); + + //Nonzero code - we could not find a matchable counter. + //Do nothing, unless extraction was forced in which case give an error + if(reason != 0) + { + static const char* reasons[24]= + { + "no problem", //0 + "counter is larger than 14 bits", //1 + "counter does not count by one", //2 + "counter uses signed math", //3 + "counter does not count by one", //4 + "ALU is not a subtractor", //5 + "ALU is not a subtractor", //6 + "ALU ports used outside counter", //7 + "ALU ports used outside counter", //8 + "ALU output used outside counter", //9 + "ALU output is not a mux", //10 + "ALU output is not full bus", //11 + "Underflow value is not constant", //12 + "No underflow detector found", //13 + "Mux output is used outside counter", //14 + "Counter reg is not DFF/ADFF", //15 + "Counter input is not full bus", //16 + "Count register is used outside counter", //17 + "Register output is not full bus", //18 + "Register output is not full bus", //19 + "No init value found", //20 + "Underflow value is not equal to init value", //21 + "Reset polarity is not positive", //22 + "Reset is not to zero" //23 + }; + + if(force_extract) + { + log_error( + "Counter extraction is set to FORCE on register %s, but a counter could not be inferred (%s)\n", + log_id(a_wire), + reasons[reason]); + } + return; + } + + //Figure out the final cell type based on the counter size + string celltype = "\\GP_COUNT8"; + if(extract.width > 8) + celltype = "\\GP_COUNT14"; + + //Log it + total_counters ++; + string reset_type = "non-resettable"; + if(extract.has_reset) + { + //TODO: support other kind of reset + reset_type = "async resettable"; + } + log(" Found %d-bit %s down counter (from %d) for register %s declared at %s\n", + extract.width, + reset_type.c_str(), + extract.count_value, + log_id(extract.rwire->name), + count_reg_src.c_str()); + + //Wipe all of the old connections to the ALU + cell->unsetPort("\\A"); + cell->unsetPort("\\B"); + cell->unsetPort("\\BI"); + cell->unsetPort("\\CI"); + cell->unsetPort("\\CO"); + cell->unsetPort("\\X"); + cell->unsetPort("\\Y"); + cell->unsetParam("\\A_SIGNED"); + cell->unsetParam("\\A_WIDTH"); + cell->unsetParam("\\B_SIGNED"); + cell->unsetParam("\\B_WIDTH"); + cell->unsetParam("\\Y_WIDTH"); + + //Change the cell type + cell->type = celltype; + + //Hook up resets + if(extract.has_reset) + { + //TODO: support other kinds of reset + cell->setParam("\\RESET_MODE", RTLIL::Const("LEVEL")); + cell->setPort("\\RST", extract.rst); + } + else + { + cell->setParam("\\RESET_MODE", RTLIL::Const("RISING")); + cell->setPort("\\RST", RTLIL::SigSpec(false)); + } + + //Hook up other stuff + cell->setParam("\\CLKIN_DIVIDE", RTLIL::Const(1)); + cell->setParam("\\COUNT_TO", RTLIL::Const(extract.count_value)); + + cell->setPort("\\CLK", extract.clk); + cell->setPort("\\OUT", extract.outsig); + + //Delete the cells we've replaced (let opt_clean handle deleting the now-redundant wires) + cells_to_remove.insert(extract.count_mux); + cells_to_remove.insert(extract.count_reg); + cells_to_remove.insert(extract.underflow_inv); +} + +struct Greenpak4CountersPass : public Pass { + Greenpak4CountersPass() : Pass("greenpak4_counters", "Extract GreenPak4 counter cells") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" greenpak4_counters [options] [selection]\n"); + log("\n"); + log("This pass converts non-resettable or async resettable down counters to GreenPak4\n"); + log("counter cells (All other GreenPak4 counter modes must be instantiated manually.)\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing GREENPAK4_COUNTERS pass (mapping counters to hard IP blocks).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-v") { + // continue; + // } + break; + } + extra_args(args, argidx, design); + + //Extract all of the counters we could find + unsigned int total_counters = 0; + for (auto module : design->selected_modules()) + { + pool<Cell*> cells_to_remove; + + ModIndex index(module); + for (auto cell : module->selected_cells()) + greenpak4_counters_worker(index, cell, total_counters, cells_to_remove); + + for(auto cell : cells_to_remove) + module->remove(cell); + } + + if(total_counters) + log("Extracted %u counters\n", total_counters); + } +} Greenpak4CountersPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/greenpak4/synth_greenpak4.cc b/techlibs/greenpak4/synth_greenpak4.cc new file mode 100644 index 000000000..50820a600 --- /dev/null +++ b/techlibs/greenpak4/synth_greenpak4.cc @@ -0,0 +1,207 @@ +/* + * 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 + +struct SynthGreenPAK4Pass : public ScriptPass +{ + SynthGreenPAK4Pass() : ScriptPass("synth_greenpak4", "synthesis for GreenPAK4 FPGAs") { } + + virtual void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" synth_greenpak4 [options]\n"); + log("\n"); + log("This command runs synthesis for GreenPAK4 FPGAs. This work is experimental.\n"); + log("\n"); + log(" -top <module>\n"); + log(" use the specified module as top module (default='top')\n"); + log("\n"); + log(" -part <part>\n"); + log(" synthesize for the specified part. Valid values are SLG46140V,\n"); + log(" SLG46620V, and SLG46621V (default).\n"); + log("\n"); + log(" -json <file>\n"); + log(" write the design to the specified JSON file. writing of an output file\n"); + log(" is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -run <from_label>:<to_label>\n"); + log(" only run the commands between the labels (see below). an empty\n"); + log(" from label is synonymous to 'begin', and empty to label is\n"); + log(" synonymous to the end of the command list.\n"); + log("\n"); + log(" -noflatten\n"); + log(" do not flatten design before synthesis\n"); + log("\n"); + log(" -retime\n"); + log(" run 'abc' with -dff option\n"); + log("\n"); + log("\n"); + log("The following commands are executed by this synthesis command:\n"); + help_script(); + log("\n"); + } + + string top_opt, part, json_file; + bool flatten, retime; + + virtual void clear_flags() YS_OVERRIDE + { + top_opt = "-auto-top"; + part = "SLG46621V"; + json_file = ""; + flatten = true; + retime = false; + } + + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + string run_from, run_to; + clear_flags(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-top" && argidx+1 < args.size()) { + top_opt = "-top " + args[++argidx]; + continue; + } + if (args[argidx] == "-json" && argidx+1 < args.size()) { + json_file = args[++argidx]; + continue; + } + if (args[argidx] == "-part" && argidx+1 < args.size()) { + part = args[++argidx]; + continue; + } + if (args[argidx] == "-run" && argidx+1 < args.size()) { + size_t pos = args[argidx+1].find(':'); + if (pos == std::string::npos) + break; + run_from = args[++argidx].substr(0, pos); + run_to = args[argidx].substr(pos+1); + continue; + } + if (args[argidx] == "-noflatten") { + flatten = false; + continue; + } + if (args[argidx] == "-retime") { + retime = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!design->full_selection()) + log_cmd_error("This comannd only operates on fully selected designs!\n"); + + if (part != "SLG46140V" && part != "SLG46620V" && part != "SLG46621V") + log_cmd_error("Invalid part name: '%s'\n", part.c_str()); + + log_header(design, "Executing SYNTH_GREENPAK4 pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + virtual void script() YS_OVERRIDE + { + if (check_label("begin")) + { + run("read_verilog -lib +/greenpak4/cells_sim.v"); + run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); + } + + if (flatten && check_label("flatten", "(unless -noflatten)")) + { + run("proc"); + run("flatten"); + run("tribuf -logic"); + } + + if (check_label("coarse")) + { + run("synth -run coarse"); + } + + if (check_label("fine")) + { + run("greenpak4_counters"); + run("clean"); + run("opt -fast -mux_undef -undriven -fine"); + run("memory_map"); + run("opt -undriven -fine"); + run("techmap"); + run("dfflibmap -prepare -liberty +/greenpak4/gp_dff.lib"); + run("opt -fast"); + if (retime || help_mode) + run("abc -dff", "(only if -retime)"); + } + + if (check_label("map_luts")) + { + if (help_mode || part == "SLG46140V") run("nlutmap -assert -luts 0,6,8,2", " (for -part SLG46140V)"); + if (help_mode || part == "SLG46620V") run("nlutmap -assert -luts 2,8,16,2", "(for -part SLG46620V)"); + if (help_mode || part == "SLG46621V") run("nlutmap -assert -luts 2,8,16,2", "(for -part SLG46621V)"); + run("clean"); + } + + if (check_label("map_cells")) + { + run("shregmap -tech greenpak4"); + run("dfflibmap -liberty +/greenpak4/gp_dff.lib"); + run("dffinit -ff GP_DFF Q INIT"); + run("dffinit -ff GP_DFFR Q INIT"); + run("dffinit -ff GP_DFFS Q INIT"); + run("dffinit -ff GP_DFFSR Q INIT"); + run("iopadmap -bits -inpad GP_IBUF OUT:IN -outpad GP_OBUF IN:OUT -inoutpad GP_OBUF OUT:IN -toutpad GP_OBUFT OE:IN:OUT -tinoutpad GP_IOBUF OE:OUT:IN:IO"); + run("techmap -map +/greenpak4/cells_map.v"); + run("clean"); + } + + if (check_label("check")) + { + run("hierarchy -check"); + run("stat"); + run("check -noinit"); + } + + if (check_label("json")) + { + run("splitnets;;", "(temporary workaround for gp4par parser limitation)"); + if (!json_file.empty() || help_mode) + run(stringf("write_json %s", help_mode ? "<file-name>" : json_file.c_str())); + } + + log_pop(); + } +} SynthGreenPAK4Pass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/ice40/.gitignore b/techlibs/ice40/.gitignore new file mode 100644 index 000000000..6bf3b6717 --- /dev/null +++ b/techlibs/ice40/.gitignore @@ -0,0 +1,4 @@ +brams_init.mk +brams_init1.vh +brams_init2.vh +brams_init3.vh diff --git a/techlibs/ice40/Makefile.inc b/techlibs/ice40/Makefile.inc new file mode 100644 index 000000000..14761c6c8 --- /dev/null +++ b/techlibs/ice40/Makefile.inc @@ -0,0 +1,33 @@ + +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 +GENFILES += techlibs/ice40/brams_init2.vh +GENFILES += techlibs/ice40/brams_init3.vh + +EXTRA_OBJS += techlibs/ice40/brams_init.mk +.SECONDARY: techlibs/ice40/brams_init.mk + +techlibs/ice40/brams_init.mk: techlibs/ice40/brams_init.py + $(Q) mkdir -p techlibs/ice40 + $(P) python3 $< + $(Q) touch techlibs/ice40/brams_init.mk + +techlibs/ice40/brams_init1.vh: techlibs/ice40/brams_init.mk +techlibs/ice40/brams_init2.vh: techlibs/ice40/brams_init.mk +techlibs/ice40/brams_init3.vh: techlibs/ice40/brams_init.mk + +$(eval $(call add_share_file,share/ice40,techlibs/ice40/arith_map.v)) +$(eval $(call add_share_file,share/ice40,techlibs/ice40/cells_map.v)) +$(eval $(call add_share_file,share/ice40,techlibs/ice40/cells_sim.v)) +$(eval $(call add_share_file,share/ice40,techlibs/ice40/latches_map.v)) +$(eval $(call add_share_file,share/ice40,techlibs/ice40/brams.txt)) +$(eval $(call add_share_file,share/ice40,techlibs/ice40/brams_map.v)) + +$(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init1.vh)) +$(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init2.vh)) +$(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init3.vh)) + diff --git a/techlibs/ice40/arith_map.v b/techlibs/ice40/arith_map.v new file mode 100644 index 000000000..4449fdc1b --- /dev/null +++ b/techlibs/ice40/arith_map.v @@ -0,0 +1,70 @@ +/* + * 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. + * + */ + +(* techmap_celltype = "$alu" *) +module _80_ice40_alu (A, B, CI, BI, X, Y, CO); + parameter A_SIGNED = 0; + parameter B_SIGNED = 0; + parameter A_WIDTH = 1; + parameter B_WIDTH = 1; + parameter Y_WIDTH = 1; + + input [A_WIDTH-1:0] A; + input [B_WIDTH-1:0] B; + output [Y_WIDTH-1:0] X, Y; + + input CI, BI; + output [Y_WIDTH-1:0] CO; + + wire _TECHMAP_FAIL_ = Y_WIDTH <= 2; + + wire [Y_WIDTH-1:0] A_buf, B_buf; + \$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(A_buf)); + \$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf)); + + wire [Y_WIDTH-1:0] AA = A_buf; + wire [Y_WIDTH-1:0] BB = BI ? ~B_buf : B_buf; + wire [Y_WIDTH-1:0] C = {CO, CI}; + + genvar i; + generate for (i = 0; i < Y_WIDTH; i = i + 1) begin:slice + SB_CARRY carry ( + .I0(AA[i]), + .I1(BB[i]), + .CI(C[i]), + .CO(CO[i]) + ); + SB_LUT4 #( + // I0: 1010 1010 1010 1010 + // I1: 1100 1100 1100 1100 + // I2: 1111 0000 1111 0000 + // I3: 1111 1111 0000 0000 + .LUT_INIT(16'b 0110_1001_1001_0110) + ) adder ( + .I0(1'b0), + .I1(AA[i]), + .I2(BB[i]), + .I3(C[i]), + .O(Y[i]) + ); + end endgenerate + + assign X = AA ^ BB; +endmodule + diff --git a/techlibs/ice40/brams.txt b/techlibs/ice40/brams.txt new file mode 100644 index 000000000..03d596111 --- /dev/null +++ b/techlibs/ice40/brams.txt @@ -0,0 +1,40 @@ +bram $__ICE40_RAM4K_M0 + init 1 + abits 8 + dbits 16 + groups 2 + ports 1 1 + wrmode 0 1 + enable 1 16 + transp 0 0 + clocks 2 3 + clkpol 2 3 +endbram + +bram $__ICE40_RAM4K_M123 + init 1 + abits 9 @M1 + dbits 8 @M1 + abits 10 @M2 + dbits 4 @M2 + abits 11 @M3 + dbits 2 @M3 + groups 2 + ports 1 1 + wrmode 0 1 + enable 1 1 + transp 0 0 + clocks 2 3 + clkpol 2 3 +endbram + +match $__ICE40_RAM4K_M0 + min efficiency 2 + make_transp + or_next_if_better +endmatch + +match $__ICE40_RAM4K_M123 + min efficiency 2 + make_transp +endmatch diff --git a/techlibs/ice40/brams_init.py b/techlibs/ice40/brams_init.py new file mode 100644 index 000000000..4a1485110 --- /dev/null +++ b/techlibs/ice40/brams_init.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +def write_init_vh(filename, initbits): + with open(filename, "w") as f: + for i in range(16): + print("localparam [255:0] INIT_%X = {" % i, file=f) + for k in range(32): + print(" %s%s" % (", ".join(["INIT[%4d]" % initbits[i*256 + 255 - k*8 - l] for l in range(8)]), "," if k != 31 else ""), file=f) + print("};", file=f); + +write_init_vh("techlibs/ice40/brams_init1.vh", [i//2 + 2048*(i%2) for i in range(4096)]) +write_init_vh("techlibs/ice40/brams_init2.vh", [i//4 + 1024*(i%4) for i in range(4096)]) +write_init_vh("techlibs/ice40/brams_init3.vh", [i//8 + 512*(i%8) for i in range(4096)]) + diff --git a/techlibs/ice40/brams_map.v b/techlibs/ice40/brams_map.v new file mode 100644 index 000000000..19a61d73b --- /dev/null +++ b/techlibs/ice40/brams_map.v @@ -0,0 +1,311 @@ + +module \$__ICE40_RAM4K ( + output [15:0] RDATA, + input RCLK, RCLKE, RE, + input [10:0] RADDR, + input WCLK, WCLKE, WE, + input [10:0] WADDR, + input [15:0] MASK, WDATA +); + parameter integer READ_MODE = 0; + parameter integer WRITE_MODE = 0; + parameter [0:0] NEGCLK_R = 0; + parameter [0:0] NEGCLK_W = 0; + + parameter [255:0] INIT_0 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter [255:0] INIT_1 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter [255:0] INIT_2 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter [255:0] INIT_3 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter [255:0] INIT_4 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter [255:0] INIT_5 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter [255:0] INIT_6 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter [255:0] INIT_7 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter [255:0] INIT_8 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter [255:0] INIT_9 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter [255:0] INIT_A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter [255:0] INIT_B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter [255:0] INIT_C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter [255:0] INIT_D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter [255:0] INIT_E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter [255:0] INIT_F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + + generate + case ({NEGCLK_R, NEGCLK_W}) + 2'b00: + SB_RAM40_4K #( + .READ_MODE(READ_MODE), + .WRITE_MODE(WRITE_MODE), + .INIT_0(INIT_0), + .INIT_1(INIT_1), + .INIT_2(INIT_2), + .INIT_3(INIT_3), + .INIT_4(INIT_4), + .INIT_5(INIT_5), + .INIT_6(INIT_6), + .INIT_7(INIT_7), + .INIT_8(INIT_8), + .INIT_9(INIT_9), + .INIT_A(INIT_A), + .INIT_B(INIT_B), + .INIT_C(INIT_C), + .INIT_D(INIT_D), + .INIT_E(INIT_E), + .INIT_F(INIT_F) + ) _TECHMAP_REPLACE_ ( + .RDATA(RDATA), + .RCLK (RCLK ), + .RCLKE(RCLKE), + .RE (RE ), + .RADDR(RADDR), + .WCLK (WCLK ), + .WCLKE(WCLKE), + .WE (WE ), + .WADDR(WADDR), + .MASK (MASK ), + .WDATA(WDATA) + ); + 2'b01: + SB_RAM40_4KNW #( + .READ_MODE(READ_MODE), + .WRITE_MODE(WRITE_MODE), + .INIT_0(INIT_0), + .INIT_1(INIT_1), + .INIT_2(INIT_2), + .INIT_3(INIT_3), + .INIT_4(INIT_4), + .INIT_5(INIT_5), + .INIT_6(INIT_6), + .INIT_7(INIT_7), + .INIT_8(INIT_8), + .INIT_9(INIT_9), + .INIT_A(INIT_A), + .INIT_B(INIT_B), + .INIT_C(INIT_C), + .INIT_D(INIT_D), + .INIT_E(INIT_E), + .INIT_F(INIT_F) + ) _TECHMAP_REPLACE_ ( + .RDATA(RDATA), + .RCLK (RCLK ), + .RCLKE(RCLKE), + .RE (RE ), + .RADDR(RADDR), + .WCLKN(WCLK ), + .WCLKE(WCLKE), + .WE (WE ), + .WADDR(WADDR), + .MASK (MASK ), + .WDATA(WDATA) + ); + 2'b10: + SB_RAM40_4KNR #( + .READ_MODE(READ_MODE), + .WRITE_MODE(WRITE_MODE), + .INIT_0(INIT_0), + .INIT_1(INIT_1), + .INIT_2(INIT_2), + .INIT_3(INIT_3), + .INIT_4(INIT_4), + .INIT_5(INIT_5), + .INIT_6(INIT_6), + .INIT_7(INIT_7), + .INIT_8(INIT_8), + .INIT_9(INIT_9), + .INIT_A(INIT_A), + .INIT_B(INIT_B), + .INIT_C(INIT_C), + .INIT_D(INIT_D), + .INIT_E(INIT_E), + .INIT_F(INIT_F) + ) _TECHMAP_REPLACE_ ( + .RDATA(RDATA), + .RCLKN(RCLK ), + .RCLKE(RCLKE), + .RE (RE ), + .RADDR(RADDR), + .WCLK (WCLK ), + .WCLKE(WCLKE), + .WE (WE ), + .WADDR(WADDR), + .MASK (MASK ), + .WDATA(WDATA) + ); + 2'b11: + SB_RAM40_4KNRNW #( + .READ_MODE(READ_MODE), + .WRITE_MODE(WRITE_MODE), + .INIT_0(INIT_0), + .INIT_1(INIT_1), + .INIT_2(INIT_2), + .INIT_3(INIT_3), + .INIT_4(INIT_4), + .INIT_5(INIT_5), + .INIT_6(INIT_6), + .INIT_7(INIT_7), + .INIT_8(INIT_8), + .INIT_9(INIT_9), + .INIT_A(INIT_A), + .INIT_B(INIT_B), + .INIT_C(INIT_C), + .INIT_D(INIT_D), + .INIT_E(INIT_E), + .INIT_F(INIT_F) + ) _TECHMAP_REPLACE_ ( + .RDATA(RDATA), + .RCLKN(RCLK ), + .RCLKE(RCLKE), + .RE (RE ), + .RADDR(RADDR), + .WCLKN(WCLK ), + .WCLKE(WCLKE), + .WE (WE ), + .WADDR(WADDR), + .MASK (MASK ), + .WDATA(WDATA) + ); + endcase + endgenerate +endmodule + + +module \$__ICE40_RAM4K_M0 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); + parameter [0:0] CLKPOL2 = 1; + parameter [0:0] CLKPOL3 = 1; + + parameter [4095:0] INIT = 4096'bx; + + input CLK2; + input CLK3; + + input [7:0] A1ADDR; + output [15:0] A1DATA; + input A1EN; + + input [7:0] B1ADDR; + input [15:0] B1DATA; + input [15:0] B1EN; + + wire [10:0] A1ADDR_11 = A1ADDR; + wire [10:0] B1ADDR_11 = B1ADDR; + + \$__ICE40_RAM4K #( + .READ_MODE(0), + .WRITE_MODE(0), + .NEGCLK_R(!CLKPOL2), + .NEGCLK_W(!CLKPOL3), + .INIT_0(INIT[ 0*256 +: 256]), + .INIT_1(INIT[ 1*256 +: 256]), + .INIT_2(INIT[ 2*256 +: 256]), + .INIT_3(INIT[ 3*256 +: 256]), + .INIT_4(INIT[ 4*256 +: 256]), + .INIT_5(INIT[ 5*256 +: 256]), + .INIT_6(INIT[ 6*256 +: 256]), + .INIT_7(INIT[ 7*256 +: 256]), + .INIT_8(INIT[ 8*256 +: 256]), + .INIT_9(INIT[ 9*256 +: 256]), + .INIT_A(INIT[10*256 +: 256]), + .INIT_B(INIT[11*256 +: 256]), + .INIT_C(INIT[12*256 +: 256]), + .INIT_D(INIT[13*256 +: 256]), + .INIT_E(INIT[14*256 +: 256]), + .INIT_F(INIT[15*256 +: 256]) + ) _TECHMAP_REPLACE_ ( + .RDATA(A1DATA), + .RADDR(A1ADDR_11), + .RCLK(CLK2), + .RCLKE(A1EN), + .RE(1'b1), + .WDATA(B1DATA), + .WADDR(B1ADDR_11), + .MASK(~B1EN), + .WCLK(CLK3), + .WCLKE(|B1EN), + .WE(1'b1) + ); +endmodule + +module \$__ICE40_RAM4K_M123 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); + parameter CFG_ABITS = 9; + parameter CFG_DBITS = 8; + + parameter [0:0] CLKPOL2 = 1; + parameter [0:0] CLKPOL3 = 1; + + parameter [4095:0] INIT = 4096'bx; + + localparam MODE = + CFG_ABITS == 9 ? 1 : + CFG_ABITS == 10 ? 2 : + CFG_ABITS == 11 ? 3 : 'bx; + + input CLK2; + input CLK3; + + input [CFG_ABITS-1:0] A1ADDR; + output [CFG_DBITS-1:0] A1DATA; + input A1EN; + + input [CFG_ABITS-1:0] B1ADDR; + input [CFG_DBITS-1:0] B1DATA; + input B1EN; + + wire [10:0] A1ADDR_11 = A1ADDR; + wire [10:0] B1ADDR_11 = B1ADDR; + + wire [15:0] A1DATA_16, B1DATA_16; + + generate + if (MODE == 1) begin + assign A1DATA = {A1DATA_16[14], A1DATA_16[12], A1DATA_16[10], A1DATA_16[ 8], + A1DATA_16[ 6], A1DATA_16[ 4], A1DATA_16[ 2], A1DATA_16[ 0]}; + assign {B1DATA_16[14], B1DATA_16[12], B1DATA_16[10], B1DATA_16[ 8], + B1DATA_16[ 6], B1DATA_16[ 4], B1DATA_16[ 2], B1DATA_16[ 0]} = B1DATA; + `include "brams_init1.vh" + end + if (MODE == 2) begin + assign A1DATA = {A1DATA_16[13], A1DATA_16[9], A1DATA_16[5], A1DATA_16[1]}; + assign {B1DATA_16[13], B1DATA_16[9], B1DATA_16[5], B1DATA_16[1]} = B1DATA; + `include "brams_init2.vh" + end + if (MODE == 3) begin + assign A1DATA = {A1DATA_16[11], A1DATA_16[3]}; + assign {B1DATA_16[11], B1DATA_16[3]} = B1DATA; + `include "brams_init3.vh" + end + endgenerate + + \$__ICE40_RAM4K #( + .READ_MODE(MODE), + .WRITE_MODE(MODE), + .NEGCLK_R(!CLKPOL2), + .NEGCLK_W(!CLKPOL3), + .INIT_0(INIT_0), + .INIT_1(INIT_1), + .INIT_2(INIT_2), + .INIT_3(INIT_3), + .INIT_4(INIT_4), + .INIT_5(INIT_5), + .INIT_6(INIT_6), + .INIT_7(INIT_7), + .INIT_8(INIT_8), + .INIT_9(INIT_9), + .INIT_A(INIT_A), + .INIT_B(INIT_B), + .INIT_C(INIT_C), + .INIT_D(INIT_D), + .INIT_E(INIT_E), + .INIT_F(INIT_F) + ) _TECHMAP_REPLACE_ ( + .RDATA(A1DATA_16), + .RADDR(A1ADDR_11), + .RCLK(CLK2), + .RCLKE(A1EN), + .RE(1'b1), + .WDATA(B1DATA_16), + .WADDR(B1ADDR_11), + .WCLK(CLK3), + .WCLKE(|B1EN), + .WE(1'b1) + ); +endmodule + diff --git a/techlibs/ice40/cells_map.v b/techlibs/ice40/cells_map.v new file mode 100644 index 000000000..0227ffadb --- /dev/null +++ b/techlibs/ice40/cells_map.v @@ -0,0 +1,57 @@ +module \$_DFF_N_ (input D, C, output Q); SB_DFFN _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C)); endmodule +module \$_DFF_P_ (input D, C, output Q); SB_DFF _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C)); endmodule + +module \$_DFFE_NN_ (input D, C, E, output Q); SB_DFFNE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(!E)); endmodule +module \$_DFFE_PN_ (input D, C, E, output Q); SB_DFFE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(!E)); endmodule + +module \$_DFFE_NP_ (input D, C, E, output Q); SB_DFFNE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E)); endmodule +module \$_DFFE_PP_ (input D, C, E, output Q); SB_DFFE _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E)); endmodule + +module \$_DFF_NN0_ (input D, C, R, output Q); SB_DFFNR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(!R)); endmodule +module \$_DFF_NN1_ (input D, C, R, output Q); SB_DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .S(!R)); endmodule +module \$_DFF_PN0_ (input D, C, R, output Q); SB_DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(!R)); endmodule +module \$_DFF_PN1_ (input D, C, R, output Q); SB_DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .S(!R)); endmodule + +module \$_DFF_NP0_ (input D, C, R, output Q); SB_DFFNR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(R)); endmodule +module \$_DFF_NP1_ (input D, C, R, output Q); SB_DFFNS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .S(R)); endmodule +module \$_DFF_PP0_ (input D, C, R, output Q); SB_DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(R)); endmodule +module \$_DFF_PP1_ (input D, C, R, output Q); SB_DFFS _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .S(R)); endmodule + +module \$__DFFE_NN0 (input D, C, E, R, output Q); SB_DFFNER _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .R(!R)); endmodule +module \$__DFFE_NN1 (input D, C, E, R, output Q); SB_DFFNES _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .S(!R)); endmodule +module \$__DFFE_PN0 (input D, C, E, R, output Q); SB_DFFER _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .R(!R)); endmodule +module \$__DFFE_PN1 (input D, C, E, R, output Q); SB_DFFES _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .S(!R)); endmodule + +module \$__DFFE_NP0 (input D, C, E, R, output Q); SB_DFFNER _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .R(R)); endmodule +module \$__DFFE_NP1 (input D, C, E, R, output Q); SB_DFFNES _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .S(R)); endmodule +module \$__DFFE_PP0 (input D, C, E, R, output Q); SB_DFFER _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .R(R)); endmodule +module \$__DFFE_PP1 (input D, C, E, R, output Q); SB_DFFES _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .E(E), .S(R)); endmodule + +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + + input [WIDTH-1:0] A; + output Y; + + generate + if (WIDTH == 1) begin + SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(1'b0), .I2(1'b0), .I3(1'b0)); + end else + if (WIDTH == 2) begin + SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(A[1]), .I2(1'b0), .I3(1'b0)); + end else + if (WIDTH == 3) begin + SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(1'b0)); + end else + if (WIDTH == 4) begin + SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3])); + end else begin + wire _TECHMAP_FAIL_ = 1; + end + endgenerate +endmodule diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v new file mode 100644 index 000000000..7778b5519 --- /dev/null +++ b/techlibs/ice40/cells_sim.v @@ -0,0 +1,883 @@ + +`define SB_DFF_REG reg Q = 0; +// `define SB_DFF_REG reg Q; + +// SiliconBlue IO Cells + +module SB_IO ( + inout PACKAGE_PIN, + input LATCH_INPUT_VALUE, + input CLOCK_ENABLE, + input INPUT_CLK, + input OUTPUT_CLK, + input OUTPUT_ENABLE, + input D_OUT_0, + input D_OUT_1, + output D_IN_0, + output D_IN_1 +); + parameter [5:0] PIN_TYPE = 6'b000000; + parameter [0:0] PULLUP = 1'b0; + parameter [0:0] NEG_TRIGGER = 1'b0; + parameter IO_STANDARD = "SB_LVCMOS"; + +`ifndef BLACKBOX + reg dout, din_0, din_1; + reg din_q_0, din_q_1; + reg dout_q_0, dout_q_1; + reg outena_q; + + generate if (!NEG_TRIGGER) begin + always @(posedge INPUT_CLK) if (CLOCK_ENABLE) din_q_0 <= PACKAGE_PIN; + always @(negedge INPUT_CLK) if (CLOCK_ENABLE) din_q_1 <= PACKAGE_PIN; + always @(posedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_0 <= D_OUT_0; + always @(negedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_1 <= D_OUT_1; + always @(posedge OUTPUT_CLK) if (CLOCK_ENABLE) outena_q <= OUTPUT_ENABLE; + end else begin + always @(negedge INPUT_CLK) if (CLOCK_ENABLE) din_q_0 <= PACKAGE_PIN; + always @(posedge INPUT_CLK) if (CLOCK_ENABLE) din_q_1 <= PACKAGE_PIN; + always @(negedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_0 <= D_OUT_0; + always @(posedge OUTPUT_CLK) if (CLOCK_ENABLE) dout_q_1 <= D_OUT_1; + always @(negedge OUTPUT_CLK) if (CLOCK_ENABLE) outena_q <= OUTPUT_ENABLE; + end endgenerate + + always @* begin + if (!PIN_TYPE[1] || !LATCH_INPUT_VALUE) + din_0 = PIN_TYPE[0] ? PACKAGE_PIN : din_q_0; + 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 = (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 = OUTPUT_ENABLE ? dout : 1'bz; + if (PIN_TYPE[5:4] == 2'b11) assign PACKAGE_PIN = outena_q ? dout : 1'bz; + endgenerate +`endif +endmodule + +module SB_GB_IO ( + inout PACKAGE_PIN, + output GLOBAL_BUFFER_OUTPUT, + input LATCH_INPUT_VALUE, + input CLOCK_ENABLE, + input INPUT_CLK, + input OUTPUT_CLK, + input OUTPUT_ENABLE, + input D_OUT_0, + input D_OUT_1, + output D_IN_0, + output D_IN_1 +); + parameter [5:0] PIN_TYPE = 6'b000000; + parameter [0:0] PULLUP = 1'b0; + parameter [0:0] NEG_TRIGGER = 1'b0; + parameter IO_STANDARD = "SB_LVCMOS"; + + assign GLOBAL_BUFFER_OUTPUT = PACKAGE_PIN; + + SB_IO #( + .PIN_TYPE(PIN_TYPE), + .PULLUP(PULLUP), + .NEG_TRIGGER(NEG_TRIGGER), + .IO_STANDARD(IO_STANDARD) + ) IO ( + .PACKAGE_PIN(PACKAGE_PIN), + .LATCH_INPUT_VALUE(LATCH_INPUT_VALUE), + .CLOCK_ENABLE(CLOCK_ENABLE), + .INPUT_CLK(INPUT_CLK), + .OUTPUT_CLK(OUTPUT_CLK), + .OUTPUT_ENABLE(OUTPUT_ENABLE), + .D_OUT_0(D_OUT_0), + .D_OUT_1(D_OUT_1), + .D_IN_0(D_IN_0), + .D_IN_1(D_IN_1) + ); +endmodule + +module SB_GB ( + input USER_SIGNAL_TO_GLOBAL_BUFFER, + output GLOBAL_BUFFER_OUTPUT +); + assign GLOBAL_BUFFER_OUTPUT = USER_SIGNAL_TO_GLOBAL_BUFFER; +endmodule + +// SiliconBlue Logic Cells + +module SB_LUT4 (output O, input I0, I1, I2, I3); + parameter [15:0] LUT_INIT = 0; + wire [7:0] s3 = I3 ? LUT_INIT[15:8] : LUT_INIT[7:0]; + wire [3:0] s2 = I2 ? s3[ 7:4] : s3[3:0]; + wire [1:0] s1 = I1 ? s2[ 3:2] : s2[1:0]; + assign O = I0 ? s1[1] : s1[0]; +endmodule + +module SB_CARRY (output CO, input I0, I1, CI); + assign CO = (I0 && I1) || ((I0 || I1) && CI); +endmodule + +// Positive Edge SiliconBlue FF Cells + +module SB_DFF (output Q, input C, D); + `SB_DFF_REG + always @(posedge C) + Q <= D; +endmodule + +module SB_DFFE (output Q, input C, E, D); + `SB_DFF_REG + always @(posedge C) + if (E) + Q <= D; +endmodule + +module SB_DFFSR (output Q, input C, R, D); + `SB_DFF_REG + always @(posedge C) + if (R) + Q <= 0; + else + Q <= D; +endmodule + +module SB_DFFR (output Q, input C, R, D); + `SB_DFF_REG + always @(posedge C, posedge R) + if (R) + Q <= 0; + else + Q <= D; +endmodule + +module SB_DFFSS (output Q, input C, S, D); + `SB_DFF_REG + always @(posedge C) + if (S) + Q <= 1; + else + Q <= D; +endmodule + +module SB_DFFS (output Q, input C, S, D); + `SB_DFF_REG + always @(posedge C, posedge S) + if (S) + Q <= 1; + else + Q <= D; +endmodule + +module SB_DFFESR (output Q, input C, E, R, D); + `SB_DFF_REG + always @(posedge C) + if (E) begin + if (R) + Q <= 0; + else + Q <= D; + end +endmodule + +module SB_DFFER (output Q, input C, E, R, D); + `SB_DFF_REG + always @(posedge C, posedge R) + if (R) + Q <= 0; + else if (E) + Q <= D; +endmodule + +module SB_DFFESS (output Q, input C, E, S, D); + `SB_DFF_REG + always @(posedge C) + if (E) begin + if (S) + Q <= 1; + else + Q <= D; + end +endmodule + +module SB_DFFES (output Q, input C, E, S, D); + `SB_DFF_REG + always @(posedge C, posedge S) + if (S) + Q <= 1; + else if (E) + Q <= D; +endmodule + +// Negative Edge SiliconBlue FF Cells + +module SB_DFFN (output Q, input C, D); + `SB_DFF_REG + always @(negedge C) + Q <= D; +endmodule + +module SB_DFFNE (output Q, input C, E, D); + `SB_DFF_REG + always @(negedge C) + if (E) + Q <= D; +endmodule + +module SB_DFFNSR (output Q, input C, R, D); + `SB_DFF_REG + always @(negedge C) + if (R) + Q <= 0; + else + Q <= D; +endmodule + +module SB_DFFNR (output Q, input C, R, D); + `SB_DFF_REG + always @(negedge C, posedge R) + if (R) + Q <= 0; + else + Q <= D; +endmodule + +module SB_DFFNSS (output Q, input C, S, D); + `SB_DFF_REG + always @(negedge C) + if (S) + Q <= 1; + else + Q <= D; +endmodule + +module SB_DFFNS (output Q, input C, S, D); + `SB_DFF_REG + always @(negedge C, posedge S) + if (S) + Q <= 1; + else + Q <= D; +endmodule + +module SB_DFFNESR (output Q, input C, E, R, D); + `SB_DFF_REG + always @(negedge C) + if (E) begin + if (R) + Q <= 0; + else + Q <= D; + end +endmodule + +module SB_DFFNER (output Q, input C, E, R, D); + `SB_DFF_REG + always @(negedge C, posedge R) + if (R) + Q <= 0; + else if (E) + Q <= D; +endmodule + +module SB_DFFNESS (output Q, input C, E, S, D); + `SB_DFF_REG + always @(negedge C) + if (E) begin + if (S) + Q <= 1; + else + Q <= D; + end +endmodule + +module SB_DFFNES (output Q, input C, E, S, D); + `SB_DFF_REG + always @(negedge C, posedge S) + if (S) + Q <= 1; + else if (E) + Q <= D; +endmodule + +// SiliconBlue RAM Cells + +module SB_RAM40_4K ( + output [15:0] RDATA, + input RCLK, RCLKE, RE, + input [10:0] RADDR, + input WCLK, WCLKE, WE, + input [10:0] WADDR, + input [15:0] MASK, WDATA +); + // MODE 0: 256 x 16 + // MODE 1: 512 x 8 + // MODE 2: 1024 x 4 + // MODE 3: 2048 x 2 + parameter WRITE_MODE = 0; + parameter READ_MODE = 0; + + parameter INIT_0 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_4 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_5 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_6 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_7 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_8 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_9 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + +`ifndef BLACKBOX + wire [15:0] WMASK_I; + wire [15:0] RMASK_I; + + reg [15:0] RDATA_I; + wire [15:0] WDATA_I; + + generate + case (WRITE_MODE) + 0: assign WMASK_I = MASK; + + 1: assign WMASK_I = WADDR[ 8] == 0 ? 16'b 1010_1010_1010_1010 : + WADDR[ 8] == 1 ? 16'b 0101_0101_0101_0101 : 16'bx; + + 2: assign WMASK_I = WADDR[ 9:8] == 0 ? 16'b 1110_1110_1110_1110 : + WADDR[ 9:8] == 1 ? 16'b 1101_1101_1101_1101 : + WADDR[ 9:8] == 2 ? 16'b 1011_1011_1011_1011 : + WADDR[ 9:8] == 3 ? 16'b 0111_0111_0111_0111 : 16'bx; + + 3: assign WMASK_I = WADDR[10:8] == 0 ? 16'b 1111_1110_1111_1110 : + WADDR[10:8] == 1 ? 16'b 1111_1101_1111_1101 : + WADDR[10:8] == 2 ? 16'b 1111_1011_1111_1011 : + WADDR[10:8] == 3 ? 16'b 1111_0111_1111_0111 : + WADDR[10:8] == 4 ? 16'b 1110_1111_1110_1111 : + WADDR[10:8] == 5 ? 16'b 1101_1111_1101_1111 : + WADDR[10:8] == 6 ? 16'b 1011_1111_1011_1111 : + WADDR[10:8] == 7 ? 16'b 0111_1111_0111_1111 : 16'bx; + endcase + + case (READ_MODE) + 0: assign RMASK_I = 16'b 0000_0000_0000_0000; + + 1: assign RMASK_I = RADDR[ 8] == 0 ? 16'b 1010_1010_1010_1010 : + RADDR[ 8] == 1 ? 16'b 0101_0101_0101_0101 : 16'bx; + + 2: assign RMASK_I = RADDR[ 9:8] == 0 ? 16'b 1110_1110_1110_1110 : + RADDR[ 9:8] == 1 ? 16'b 1101_1101_1101_1101 : + RADDR[ 9:8] == 2 ? 16'b 1011_1011_1011_1011 : + RADDR[ 9:8] == 3 ? 16'b 0111_0111_0111_0111 : 16'bx; + + 3: assign RMASK_I = RADDR[10:8] == 0 ? 16'b 1111_1110_1111_1110 : + RADDR[10:8] == 1 ? 16'b 1111_1101_1111_1101 : + RADDR[10:8] == 2 ? 16'b 1111_1011_1111_1011 : + RADDR[10:8] == 3 ? 16'b 1111_0111_1111_0111 : + RADDR[10:8] == 4 ? 16'b 1110_1111_1110_1111 : + RADDR[10:8] == 5 ? 16'b 1101_1111_1101_1111 : + RADDR[10:8] == 6 ? 16'b 1011_1111_1011_1111 : + RADDR[10:8] == 7 ? 16'b 0111_1111_0111_1111 : 16'bx; + endcase + + case (WRITE_MODE) + 0: assign WDATA_I = WDATA; + + 1: assign WDATA_I = {WDATA[14], WDATA[14], WDATA[12], WDATA[12], + WDATA[10], WDATA[10], WDATA[ 8], WDATA[ 8], + WDATA[ 6], WDATA[ 6], WDATA[ 4], WDATA[ 4], + WDATA[ 2], WDATA[ 2], WDATA[ 0], WDATA[ 0]}; + + 2: assign WDATA_I = {WDATA[13], WDATA[13], WDATA[13], WDATA[13], + WDATA[ 9], WDATA[ 9], WDATA[ 9], WDATA[ 9], + WDATA[ 5], WDATA[ 5], WDATA[ 5], WDATA[ 5], + WDATA[ 1], WDATA[ 1], WDATA[ 1], WDATA[ 1]}; + + 3: assign WDATA_I = {WDATA[11], WDATA[11], WDATA[11], WDATA[11], + WDATA[11], WDATA[11], WDATA[11], WDATA[11], + WDATA[ 3], WDATA[ 3], WDATA[ 3], WDATA[ 3], + WDATA[ 3], WDATA[ 3], WDATA[ 3], WDATA[ 3]}; + endcase + + case (READ_MODE) + 0: assign RDATA = RDATA_I; + 1: assign RDATA = {1'b0, |RDATA_I[15:14], 1'b0, |RDATA_I[13:12], 1'b0, |RDATA_I[11:10], 1'b0, |RDATA_I[ 9: 8], + 1'b0, |RDATA_I[ 7: 6], 1'b0, |RDATA_I[ 5: 4], 1'b0, |RDATA_I[ 3: 2], 1'b0, |RDATA_I[ 1: 0]}; + 2: assign RDATA = {2'b0, |RDATA_I[15:12], 3'b0, |RDATA_I[11: 8], 3'b0, |RDATA_I[ 7: 4], 3'b0, |RDATA_I[ 3: 0], 1'b0}; + 3: assign RDATA = {4'b0, |RDATA_I[15: 8], 7'b0, |RDATA_I[ 7: 0], 3'b0}; + endcase + endgenerate + + integer i; + reg [15:0] memory [0:255]; + + initial begin + for (i=0; i<16; i=i+1) begin + memory[ 0*16 + i] <= INIT_0[16*i +: 16]; + memory[ 1*16 + i] <= INIT_1[16*i +: 16]; + memory[ 2*16 + i] <= INIT_2[16*i +: 16]; + memory[ 3*16 + i] <= INIT_3[16*i +: 16]; + memory[ 4*16 + i] <= INIT_4[16*i +: 16]; + memory[ 5*16 + i] <= INIT_5[16*i +: 16]; + memory[ 6*16 + i] <= INIT_6[16*i +: 16]; + memory[ 7*16 + i] <= INIT_7[16*i +: 16]; + memory[ 8*16 + i] <= INIT_8[16*i +: 16]; + memory[ 9*16 + i] <= INIT_9[16*i +: 16]; + memory[10*16 + i] <= INIT_A[16*i +: 16]; + memory[11*16 + i] <= INIT_B[16*i +: 16]; + memory[12*16 + i] <= INIT_C[16*i +: 16]; + memory[13*16 + i] <= INIT_D[16*i +: 16]; + memory[14*16 + i] <= INIT_E[16*i +: 16]; + memory[15*16 + i] <= INIT_F[16*i +: 16]; + end + end + + always @(posedge WCLK) begin + if (WE && WCLKE) begin + if (!WMASK_I[ 0]) memory[WADDR[7:0]][ 0] <= WDATA_I[ 0]; + if (!WMASK_I[ 1]) memory[WADDR[7:0]][ 1] <= WDATA_I[ 1]; + if (!WMASK_I[ 2]) memory[WADDR[7:0]][ 2] <= WDATA_I[ 2]; + if (!WMASK_I[ 3]) memory[WADDR[7:0]][ 3] <= WDATA_I[ 3]; + if (!WMASK_I[ 4]) memory[WADDR[7:0]][ 4] <= WDATA_I[ 4]; + if (!WMASK_I[ 5]) memory[WADDR[7:0]][ 5] <= WDATA_I[ 5]; + if (!WMASK_I[ 6]) memory[WADDR[7:0]][ 6] <= WDATA_I[ 6]; + if (!WMASK_I[ 7]) memory[WADDR[7:0]][ 7] <= WDATA_I[ 7]; + if (!WMASK_I[ 8]) memory[WADDR[7:0]][ 8] <= WDATA_I[ 8]; + if (!WMASK_I[ 9]) memory[WADDR[7:0]][ 9] <= WDATA_I[ 9]; + if (!WMASK_I[10]) memory[WADDR[7:0]][10] <= WDATA_I[10]; + if (!WMASK_I[11]) memory[WADDR[7:0]][11] <= WDATA_I[11]; + if (!WMASK_I[12]) memory[WADDR[7:0]][12] <= WDATA_I[12]; + if (!WMASK_I[13]) memory[WADDR[7:0]][13] <= WDATA_I[13]; + if (!WMASK_I[14]) memory[WADDR[7:0]][14] <= WDATA_I[14]; + if (!WMASK_I[15]) memory[WADDR[7:0]][15] <= WDATA_I[15]; + end + end + + always @(posedge RCLK) begin + if (RE && RCLKE) begin + RDATA_I <= memory[RADDR[7:0]] & ~RMASK_I; + end + end +`endif +endmodule + +module SB_RAM40_4KNR ( + output [15:0] RDATA, + input RCLKN, RCLKE, RE, + input [10:0] RADDR, + input WCLK, WCLKE, WE, + input [10:0] WADDR, + input [15:0] MASK, WDATA +); + parameter WRITE_MODE = 0; + parameter READ_MODE = 0; + + parameter INIT_0 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_4 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_5 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_6 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_7 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_8 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_9 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + + SB_RAM40_4K #( + .WRITE_MODE(WRITE_MODE), + .READ_MODE (READ_MODE ), + .INIT_0 (INIT_0 ), + .INIT_1 (INIT_1 ), + .INIT_2 (INIT_2 ), + .INIT_3 (INIT_3 ), + .INIT_4 (INIT_4 ), + .INIT_5 (INIT_5 ), + .INIT_6 (INIT_6 ), + .INIT_7 (INIT_7 ), + .INIT_8 (INIT_8 ), + .INIT_9 (INIT_9 ), + .INIT_A (INIT_A ), + .INIT_B (INIT_B ), + .INIT_C (INIT_C ), + .INIT_D (INIT_D ), + .INIT_E (INIT_E ), + .INIT_F (INIT_F ) + ) RAM ( + .RDATA(RDATA), + .RCLK (~RCLKN), + .RCLKE(RCLKE), + .RE (RE ), + .RADDR(RADDR), + .WCLK (WCLK ), + .WCLKE(WCLKE), + .WE (WE ), + .WADDR(WADDR), + .MASK (MASK ), + .WDATA(WDATA) + ); +endmodule + +module SB_RAM40_4KNW ( + output [15:0] RDATA, + input RCLK, RCLKE, RE, + input [10:0] RADDR, + input WCLKN, WCLKE, WE, + input [10:0] WADDR, + input [15:0] MASK, WDATA +); + parameter WRITE_MODE = 0; + parameter READ_MODE = 0; + + parameter INIT_0 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_4 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_5 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_6 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_7 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_8 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_9 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + + SB_RAM40_4K #( + .WRITE_MODE(WRITE_MODE), + .READ_MODE (READ_MODE ), + .INIT_0 (INIT_0 ), + .INIT_1 (INIT_1 ), + .INIT_2 (INIT_2 ), + .INIT_3 (INIT_3 ), + .INIT_4 (INIT_4 ), + .INIT_5 (INIT_5 ), + .INIT_6 (INIT_6 ), + .INIT_7 (INIT_7 ), + .INIT_8 (INIT_8 ), + .INIT_9 (INIT_9 ), + .INIT_A (INIT_A ), + .INIT_B (INIT_B ), + .INIT_C (INIT_C ), + .INIT_D (INIT_D ), + .INIT_E (INIT_E ), + .INIT_F (INIT_F ) + ) RAM ( + .RDATA(RDATA), + .RCLK (RCLK ), + .RCLKE(RCLKE), + .RE (RE ), + .RADDR(RADDR), + .WCLK (~WCLKN), + .WCLKE(WCLKE), + .WE (WE ), + .WADDR(WADDR), + .MASK (MASK ), + .WDATA(WDATA) + ); +endmodule + +module SB_RAM40_4KNRNW ( + output [15:0] RDATA, + input RCLKN, RCLKE, RE, + input [10:0] RADDR, + input WCLKN, WCLKE, WE, + input [10:0] WADDR, + input [15:0] MASK, WDATA +); + parameter WRITE_MODE = 0; + parameter READ_MODE = 0; + + parameter INIT_0 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_4 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_5 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_6 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_7 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_8 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_9 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + + SB_RAM40_4K #( + .WRITE_MODE(WRITE_MODE), + .READ_MODE (READ_MODE ), + .INIT_0 (INIT_0 ), + .INIT_1 (INIT_1 ), + .INIT_2 (INIT_2 ), + .INIT_3 (INIT_3 ), + .INIT_4 (INIT_4 ), + .INIT_5 (INIT_5 ), + .INIT_6 (INIT_6 ), + .INIT_7 (INIT_7 ), + .INIT_8 (INIT_8 ), + .INIT_9 (INIT_9 ), + .INIT_A (INIT_A ), + .INIT_B (INIT_B ), + .INIT_C (INIT_C ), + .INIT_D (INIT_D ), + .INIT_E (INIT_E ), + .INIT_F (INIT_F ) + ) RAM ( + .RDATA(RDATA), + .RCLK (~RCLKN), + .RCLKE(RCLKE), + .RE (RE ), + .RADDR(RADDR), + .WCLK (~WCLKN), + .WCLKE(WCLKE), + .WE (WE ), + .WADDR(WADDR), + .MASK (MASK ), + .WDATA(WDATA) + ); +endmodule + +// Packed IceStorm Logic Cells + +module ICESTORM_LC ( + input I0, I1, I2, I3, CIN, CLK, CEN, SR, + output LO, O, COUT +); + parameter [15:0] LUT_INIT = 0; + + parameter [0:0] NEG_CLK = 0; + parameter [0:0] CARRY_ENABLE = 0; + parameter [0:0] DFF_ENABLE = 0; + parameter [0:0] SET_NORESET = 0; + parameter [0:0] ASYNC_SR = 0; + + wire COUT = CARRY_ENABLE ? (I1 && I2) || ((I1 || I2) && CIN) : 1'bx; + + wire [7:0] lut_s3 = I3 ? LUT_INIT[15:8] : LUT_INIT[7:0]; + wire [3:0] lut_s2 = I2 ? lut_s3[ 7:4] : lut_s3[3:0]; + 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; + + reg o_reg; + always @(posedge polarized_clk) + if (CEN) + o_reg <= SR ? SET_NORESET : lut_o; + + reg o_reg_async; + always @(posedge polarized_clk, posedge SR) + if (SR) + o_reg <= SET_NORESET; + else if (CEN) + o_reg <= lut_o; + + assign O = DFF_ENABLE ? ASYNC_SR ? o_reg_async : o_reg : lut_o; +endmodule + +// SiliconBlue PLL Cells + +(* blackbox *) +module SB_PLL40_CORE ( + input REFERENCECLK, + output PLLOUTCORE, + output PLLOUTGLOBAL, + input EXTFEEDBACK, + input [7:0] DYNAMICDELAY, + output LOCK, + input BYPASS, + input RESETB, + input LATCHINPUTVALUE, + output SDO, + input SDI, + input SCLK +); + parameter FEEDBACK_PATH = "SIMPLE"; + parameter DELAY_ADJUSTMENT_MODE_FEEDBACK = "FIXED"; + parameter DELAY_ADJUSTMENT_MODE_RELATIVE = "FIXED"; + parameter SHIFTREG_DIV_MODE = 1'b0; + parameter FDA_FEEDBACK = 4'b0000; + parameter FDA_RELATIVE = 4'b0000; + parameter PLLOUT_SELECT = "GENCLK"; + parameter DIVR = 4'b0000; + parameter DIVF = 7'b0000000; + parameter DIVQ = 3'b000; + parameter FILTER_RANGE = 3'b000; + parameter ENABLE_ICEGATE = 1'b0; + parameter TEST_MODE = 1'b0; + parameter EXTERNAL_DIVIDE_FACTOR = 1; +endmodule + +(* blackbox *) +module SB_PLL40_PAD ( + input PACKAGEPIN, + output PLLOUTCORE, + output PLLOUTGLOBAL, + input EXTFEEDBACK, + input [7:0] DYNAMICDELAY, + output LOCK, + input BYPASS, + input RESETB, + input LATCHINPUTVALUE, + output SDO, + input SDI, + input SCLK +); + parameter FEEDBACK_PATH = "SIMPLE"; + parameter DELAY_ADJUSTMENT_MODE_FEEDBACK = "FIXED"; + parameter DELAY_ADJUSTMENT_MODE_RELATIVE = "FIXED"; + parameter SHIFTREG_DIV_MODE = 1'b0; + parameter FDA_FEEDBACK = 4'b0000; + parameter FDA_RELATIVE = 4'b0000; + parameter PLLOUT_SELECT = "GENCLK"; + parameter DIVR = 4'b0000; + parameter DIVF = 7'b0000000; + parameter DIVQ = 3'b000; + parameter FILTER_RANGE = 3'b000; + parameter ENABLE_ICEGATE = 1'b0; + parameter TEST_MODE = 1'b0; + parameter EXTERNAL_DIVIDE_FACTOR = 1; +endmodule + +(* blackbox *) +module SB_PLL40_2_PAD ( + input PACKAGEPIN, + output PLLOUTCOREA, + output PLLOUTGLOBALA, + output PLLOUTCOREB, + output PLLOUTGLOBALB, + input EXTFEEDBACK, + input [7:0] DYNAMICDELAY, + output LOCK, + input BYPASS, + input RESETB, + input LATCHINPUTVALUE, + output SDO, + input SDI, + input SCLK +); + parameter FEEDBACK_PATH = "SIMPLE"; + parameter DELAY_ADJUSTMENT_MODE_FEEDBACK = "FIXED"; + parameter DELAY_ADJUSTMENT_MODE_RELATIVE = "FIXED"; + parameter SHIFTREG_DIV_MODE = 1'b0; + parameter FDA_FEEDBACK = 4'b0000; + parameter FDA_RELATIVE = 4'b0000; + parameter PLLOUT_SELECT_PORTB = "GENCLK"; + parameter DIVR = 4'b0000; + parameter DIVF = 7'b0000000; + parameter DIVQ = 3'b000; + parameter FILTER_RANGE = 3'b000; + parameter ENABLE_ICEGATE_PORTA = 1'b0; + parameter ENABLE_ICEGATE_PORTB = 1'b0; + parameter TEST_MODE = 1'b0; + parameter EXTERNAL_DIVIDE_FACTOR = 1; +endmodule + +(* blackbox *) +module SB_PLL40_2F_CORE ( + input REFERENCECLK, + output PLLOUTCOREA, + output PLLOUTGLOBALA, + output PLLOUTCOREB, + output PLLOUTGLOBALB, + input EXTFEEDBACK, + input [7:0] DYNAMICDELAY, + output LOCK, + input BYPASS, + input RESETB, + input LATCHINPUTVALUE, + output SDO, + input SDI, + input SCLK +); + parameter FEEDBACK_PATH = "SIMPLE"; + parameter DELAY_ADJUSTMENT_MODE_FEEDBACK = "FIXED"; + parameter DELAY_ADJUSTMENT_MODE_RELATIVE = "FIXED"; + parameter SHIFTREG_DIV_MODE = 1'b0; + parameter FDA_FEEDBACK = 4'b0000; + parameter FDA_RELATIVE = 4'b0000; + parameter PLLOUT_SELECT_PORTA = "GENCLK"; + parameter PLLOUT_SELECT_PORTB = "GENCLK"; + parameter DIVR = 4'b0000; + parameter DIVF = 7'b0000000; + parameter DIVQ = 3'b000; + parameter FILTER_RANGE = 3'b000; + parameter ENABLE_ICEGATE_PORTA = 1'b0; + parameter ENABLE_ICEGATE_PORTB = 1'b0; + parameter TEST_MODE = 1'b0; + parameter EXTERNAL_DIVIDE_FACTOR = 1; +endmodule + +(* blackbox *) +module SB_PLL40_2F_PAD ( + input PACKAGEPIN, + output PLLOUTCOREA, + output PLLOUTGLOBALA, + output PLLOUTCOREB, + output PLLOUTGLOBALB, + input EXTFEEDBACK, + input [7:0] DYNAMICDELAY, + output LOCK, + input BYPASS, + input RESETB, + input LATCHINPUTVALUE, + output SDO, + input SDI, + input SCLK +); + parameter FEEDBACK_PATH = "SIMPLE"; + parameter DELAY_ADJUSTMENT_MODE_FEEDBACK = "FIXED"; + parameter DELAY_ADJUSTMENT_MODE_RELATIVE = "FIXED"; + parameter SHIFTREG_DIV_MODE = 2'b00; + parameter FDA_FEEDBACK = 4'b0000; + parameter FDA_RELATIVE = 4'b0000; + parameter PLLOUT_SELECT_PORTA = "GENCLK"; + parameter PLLOUT_SELECT_PORTB = "GENCLK"; + parameter DIVR = 4'b0000; + parameter DIVF = 7'b0000000; + parameter DIVQ = 3'b000; + parameter FILTER_RANGE = 3'b000; + parameter ENABLE_ICEGATE_PORTA = 1'b0; + parameter ENABLE_ICEGATE_PORTB = 1'b0; + parameter TEST_MODE = 1'b0; + parameter EXTERNAL_DIVIDE_FACTOR = 1; +endmodule + +// SiliconBlue Device Configuration Cells + +(* blackbox, keep *) +module SB_WARMBOOT ( + input BOOT, + input S1, + input S0 +); +endmodule diff --git a/techlibs/ice40/ice40_ffinit.cc b/techlibs/ice40/ice40_ffinit.cc new file mode 100644 index 000000000..8a2c30d6a --- /dev/null +++ b/techlibs/ice40/ice40_ffinit.cc @@ -0,0 +1,167 @@ +/* + * 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(design, "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; + dict<SigBit, SigBit> initbit_to_wire; + 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 (%s = %s, %s = %s).\n", + log_signal(bit), log_signal(SigBit(wire, i)), log_signal(val), + log_signal(initbit_to_wire[bit]), log_signal(initbits.at(bit))); + continue; + } + + initbits[bit] = val; + initbit_to_wire[bit] = SigBit(wire, i); + } + } + + 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_ffssr.cc b/techlibs/ice40/ice40_ffssr.cc new file mode 100644 index 000000000..9a6d69df0 --- /dev/null +++ b/techlibs/ice40/ice40_ffssr.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/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct Ice40FfssrPass : public Pass { + Ice40FfssrPass() : Pass("ice40_ffssr", "iCE40: merge synchronous set/reset into FF cells") { } + virtual void help() + { + log("\n"); + log(" ice40_ffssr [options] [selection]\n"); + log("\n"); + log("Merge synchronous set/reset $_MUX_ cells into iCE40 FFs.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + log_header(design, "Executing ICE40_FFSSR pass (merge synchronous set/reset into FF cells).\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); + + pool<IdString> sb_dff_types; + sb_dff_types.insert("\\SB_DFF"); + sb_dff_types.insert("\\SB_DFFE"); + sb_dff_types.insert("\\SB_DFFN"); + sb_dff_types.insert("\\SB_DFFNE"); + + for (auto module : design->selected_modules()) + { + log("Merging set/reset $_MUX_ cells into SB_FFs in %s.\n", log_id(module)); + + SigMap sigmap(module); + dict<SigBit, Cell*> sr_muxes; + vector<Cell*> ff_cells; + + for (auto cell : module->selected_cells()) + { + if (sb_dff_types.count(cell->type)) { + ff_cells.push_back(cell); + continue; + } + + if (cell->type != "$_MUX_") + continue; + + SigBit bit_a = sigmap(cell->getPort("\\A")); + SigBit bit_b = sigmap(cell->getPort("\\B")); + + if (bit_a.wire == nullptr || bit_b.wire == nullptr) + sr_muxes[sigmap(cell->getPort("\\Y"))] = cell; + } + + for (auto cell : ff_cells) + { + SigBit bit_d = sigmap(cell->getPort("\\D")); + + if (sr_muxes.count(bit_d) == 0) + continue; + + Cell *mux_cell = sr_muxes.at(bit_d); + SigBit bit_a = sigmap(mux_cell->getPort("\\A")); + SigBit bit_b = sigmap(mux_cell->getPort("\\B")); + SigBit bit_s = sigmap(mux_cell->getPort("\\S")); + + log(" Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell), + log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type)); + + SigBit sr_val, sr_sig; + if (bit_a.wire == nullptr) { + bit_d = bit_b; + sr_val = bit_a; + sr_sig = module->NotGate(NEW_ID, bit_s); + } else { + log_assert(bit_b.wire == nullptr); + bit_d = bit_a; + sr_val = bit_b; + sr_sig = bit_s; + } + + if (sr_val == State::S1) { + cell->type = cell->type.str() + "SS"; + cell->setPort("\\S", sr_sig); + cell->setPort("\\D", bit_d); + } else { + cell->type = cell->type.str() + "SR"; + cell->setPort("\\R", sr_sig); + cell->setPort("\\D", bit_d); + } + } + } + } +} Ice40FfssrPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/ice40/ice40_opt.cc b/techlibs/ice40/ice40_opt.cc new file mode 100644 index 000000000..ae72f5d64 --- /dev/null +++ b/techlibs/ice40/ice40_opt.cc @@ -0,0 +1,193 @@ +/* + * 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" +#include "passes/techmap/simplemap.h" +#include <stdlib.h> +#include <stdio.h> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +static void run_ice40_opts(Module *module, bool unlut_mode) +{ + pool<SigBit> optimized_co; + vector<Cell*> sb_lut_cells; + SigMap sigmap(module); + + for (auto cell : module->selected_cells()) + { + if (cell->type == "\\SB_LUT4") + { + sb_lut_cells.push_back(cell); + continue; + } + + if (cell->type == "\\SB_CARRY") + { + SigSpec non_const_inputs, replacement_output; + int count_zeros = 0, count_ones = 0; + + SigBit inbit[3] = {cell->getPort("\\I0"), cell->getPort("\\I1"), cell->getPort("\\CI")}; + for (int i = 0; i < 3; i++) + if (inbit[i].wire == nullptr) { + if (inbit[i] == State::S1) + count_ones++; + else + count_zeros++; + } else + non_const_inputs.append(inbit[i]); + + if (count_zeros >= 2) + replacement_output = State::S0; + else if (count_ones >= 2) + replacement_output = State::S1; + else if (GetSize(non_const_inputs) == 1) + replacement_output = non_const_inputs; + + if (GetSize(replacement_output)) { + optimized_co.insert(sigmap(cell->getPort("\\CO"))); + module->connect(cell->getPort("\\CO"), replacement_output); + module->design->scratchpad_set_bool("opt.did_something", true); + log("Optimized away SB_CARRY cell %s.%s: CO=%s\n", + log_id(module), log_id(cell), log_signal(replacement_output)); + module->remove(cell); + } + continue; + } + } + + for (auto cell : sb_lut_cells) + { + 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 (unlut_mode) + goto remap_lut; + + 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"; + cell->setParam("\\WIDTH", 4); + cell->setParam("\\LUT", cell->getParam("\\LUT_INIT")); + cell->unsetParam("\\LUT_INIT"); + + cell->setPort("\\A", SigSpec({cell->getPort("\\I3"), cell->getPort("\\I2"), cell->getPort("\\I1"), cell->getPort("\\I0")})); + cell->setPort("\\Y", cell->getPort("\\O")); + cell->unsetPort("\\I0"); + cell->unsetPort("\\I1"); + cell->unsetPort("\\I2"); + cell->unsetPort("\\I3"); + cell->unsetPort("\\O"); + + cell->check(); + simplemap_lut(module, cell); + module->remove(cell); + } +} + +struct Ice40OptPass : public Pass { + Ice40OptPass() : Pass("ice40_opt", "iCE40: perform simple optimizations") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" ice40_opt [options] [selection]\n"); + log("\n"); + log("This command executes the following script:\n"); + log("\n"); + log(" do\n"); + log(" <ice40 specific optimizations>\n"); + log(" opt_expr -mux_undef -undriven [-full]\n"); + log(" opt_merge\n"); + log(" opt_rmdff\n"); + log(" opt_clean\n"); + log(" while <changed design>\n"); + log("\n"); + log("When called with the option -unlut, this command will transform all already\n"); + log("mapped SB_LUT4 cells back to logic.\n"); + log("\n"); + } + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + { + string opt_expr_args = "-mux_undef -undriven"; + bool unlut_mode = false; + + log_header(design, "Executing ICE40_OPT pass (performing simple optimizations).\n"); + log_push(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-full") { + opt_expr_args += " -full"; + continue; + } + if (args[argidx] == "-unlut") { + unlut_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + while (1) + { + design->scratchpad_unset("opt.did_something"); + + log_header(design, "Running ICE40 specific optimizations.\n"); + for (auto module : design->selected_modules()) + run_ice40_opts(module, unlut_mode); + + Pass::call(design, "opt_expr " + opt_expr_args); + Pass::call(design, "opt_merge"); + Pass::call(design, "opt_rmdff"); + Pass::call(design, "opt_clean"); + + if (design->scratchpad_get_bool("opt.did_something") == false) + break; + + log_header(design, "Rerunning OPT passes. (Removed registers in this run.)\n"); + } + + design->optimize(); + design->sort(); + design->check(); + + log_header(design, "Finished OPT passes. (There is nothing left to do.)\n"); + log_pop(); + } +} Ice40OptPass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/ice40/latches_map.v b/techlibs/ice40/latches_map.v new file mode 100644 index 000000000..c28f88cf7 --- /dev/null +++ b/techlibs/ice40/latches_map.v @@ -0,0 +1,11 @@ +module \$_DLATCH_N_ (E, D, Q); + wire [1023:0] _TECHMAP_DO_ = "simplemap; opt"; + input E, D; + output Q = !E ? D : Q; +endmodule + +module \$_DLATCH_P_ (E, D, Q); + wire [1023:0] _TECHMAP_DO_ = "simplemap; opt"; + input E, D; + output Q = E ? D : Q; +endmodule diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc new file mode 100644 index 000000000..38a9cf9d6 --- /dev/null +++ b/techlibs/ice40/synth_ice40.cc @@ -0,0 +1,250 @@ +/* + * 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 + +struct SynthIce40Pass : public ScriptPass +{ + SynthIce40Pass() : ScriptPass("synth_ice40", "synthesis for iCE40 FPGAs") { } + + virtual void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" synth_ice40 [options]\n"); + log("\n"); + log("This command runs synthesis for iCE40 FPGAs. This work is experimental.\n"); + log("\n"); + log(" -top <module>\n"); + log(" use the specified module as top module (default='top')\n"); + log("\n"); + log(" -blif <file>\n"); + log(" write the design to the specified BLIF file. writing of an output file\n"); + log(" is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -edif <file>\n"); + log(" write the design to the specified edif file. writing of an output file\n"); + log(" is omitted if this parameter is not specified.\n"); + 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(" -noflatten\n"); + log(" do not flatten design before synthesis\n"); + log("\n"); + log(" -retime\n"); + log(" run 'abc' with -dff option\n"); + log("\n"); + log(" -nocarry\n"); + log(" do not use SB_CARRY cells in output netlist\n"); + log("\n"); + 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"); + help_script(); + log("\n"); + } + + string top_opt, blif_file, edif_file; + bool nocarry, nobram, flatten, retime, abc2; + + virtual void clear_flags() YS_OVERRIDE + { + top_opt = "-auto-top"; + blif_file = ""; + edif_file = ""; + nocarry = false; + nobram = false; + flatten = true; + retime = false; + abc2 = false; + } + + virtual void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + string run_from, run_to; + clear_flags(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-top" && argidx+1 < args.size()) { + top_opt = "-top " + args[++argidx]; + continue; + } + if (args[argidx] == "-blif" && argidx+1 < args.size()) { + blif_file = args[++argidx]; + continue; + } + if (args[argidx] == "-edif" && argidx+1 < args.size()) { + edif_file = args[++argidx]; + continue; + } + if (args[argidx] == "-run" && argidx+1 < args.size()) { + size_t pos = args[argidx+1].find(':'); + if (pos == std::string::npos) + break; + run_from = args[++argidx].substr(0, pos); + run_to = args[argidx].substr(pos+1); + continue; + } + if (args[argidx] == "-flatten") { + flatten = true; + continue; + } + if (args[argidx] == "-noflatten") { + flatten = false; + continue; + } + if (args[argidx] == "-retime") { + retime = true; + continue; + } + if (args[argidx] == "-nocarry") { + nocarry = true; + continue; + } + if (args[argidx] == "-nobram") { + nobram = true; + continue; + } + if (args[argidx] == "-abc2") { + abc2 = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!design->full_selection()) + log_cmd_error("This comannd only operates on fully selected designs!\n"); + + log_header(design, "Executing SYNTH_ICE40 pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + virtual void script() YS_OVERRIDE + { + if (check_label("begin")) + { + run("read_verilog -lib +/ice40/cells_sim.v"); + run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); + } + + if (flatten && check_label("flatten", "(unless -noflatten)")) + { + run("proc"); + run("flatten"); + run("tribuf -logic"); + run("deminout"); + } + + if (check_label("coarse")) + { + run("synth -run coarse"); + } + + if (!nobram && check_label("bram", "(skip if -nobram)")) + { + run("memory_bram -rules +/ice40/brams.txt"); + run("techmap -map +/ice40/brams_map.v"); + } + + if (check_label("fine")) + { + run("opt -fast -mux_undef -undriven -fine"); + run("memory_map"); + run("opt -undriven -fine"); + if (nocarry) + run("techmap"); + else + run("techmap -map +/techmap.v -map +/ice40/arith_map.v"); + if (retime || help_mode) + run("abc -dff", "(only if -retime)"); + run("ice40_opt"); + } + + if (check_label("map_ffs")) + { + run("dffsr2dff"); + run("dff2dffe -direct-match $_DFF_*"); + run("techmap -map +/ice40/cells_map.v"); + run("opt_expr -mux_undef"); + run("simplemap"); + run("ice40_ffinit"); + run("ice40_ffssr"); + run("ice40_opt -full"); + } + + if (check_label("map_luts")) + { + if (abc2 || help_mode) { + run("abc", " (only if -abc2)"); + run("ice40_opt", "(only if -abc2)"); + } + run("techmap -map +/ice40/latches_map.v"); + run("abc -lut 4"); + run("clean"); + } + + if (check_label("map_cells")) + { + run("techmap -map +/ice40/cells_map.v"); + run("clean"); + } + + if (check_label("check")) + { + run("hierarchy -check"); + run("stat"); + run("check -noinit"); + } + + if (check_label("blif")) + { + if (!blif_file.empty() || help_mode) + run(stringf("write_blif -gates -attr -param %s", help_mode ? "<file-name>" : blif_file.c_str())); + } + + if (check_label("edif")) + { + if (!edif_file.empty() || help_mode) + run(stringf("write_edif %s", help_mode ? "<file-name>" : edif_file.c_str())); + } + } +} SynthIce40Pass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/ice40/tests/.gitignore b/techlibs/ice40/tests/.gitignore new file mode 100644 index 000000000..b58f9ad4a --- /dev/null +++ b/techlibs/ice40/tests/.gitignore @@ -0,0 +1,2 @@ +test_ffs_[01][01][01][01][01]_* +test_bram_[0-9]* diff --git a/techlibs/ice40/tests/test_arith.v b/techlibs/ice40/tests/test_arith.v new file mode 100644 index 000000000..77f79b973 --- /dev/null +++ b/techlibs/ice40/tests/test_arith.v @@ -0,0 +1,3 @@ +module test(input [4:0] a, b, c, output [4:0] y); + assign y = ((a+b) ^ (a-c)) - ((a*b) + (a*c) - (b*c)); +endmodule diff --git a/techlibs/ice40/tests/test_arith.ys b/techlibs/ice40/tests/test_arith.ys new file mode 100644 index 000000000..160c767fb --- /dev/null +++ b/techlibs/ice40/tests/test_arith.ys @@ -0,0 +1,10 @@ +read_verilog test_arith.v +synth_ice40 +techmap -map ../cells_sim.v +rename test gate + +read_verilog test_arith.v +rename test gold + +miter -equiv -flatten -make_outputs gold gate miter +sat -verify -prove trigger 0 -show-ports miter diff --git a/techlibs/ice40/tests/test_bram.sh b/techlibs/ice40/tests/test_bram.sh new file mode 100644 index 000000000..d4d641a9c --- /dev/null +++ b/techlibs/ice40/tests/test_bram.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -ex + +for abits in 7 8 9 10 11 12; do +for dbits in 2 4 8 16 24 32; do + id="test_bram_${abits}_${dbits}" + iadr=$((RANDOM % (1 << abits))) + idat=$((RANDOM % ((1 << dbits) - 1) + 1)) + sed -re "s/(ABITS = )0/\1$abits/g; s/(DBITS = )0/\1$dbits/g; s/(INIT_ADDR = )0/\1$iadr/g; s/(INIT_DATA = )0/\1$idat/g;" < test_bram.v > ${id}.v + sed -re "s/(ABITS = )0/\1$abits/g; s/(DBITS = )0/\1$dbits/g; s/(INIT_ADDR = )0/\1$iadr/g; s/(INIT_DATA = )0/\1$idat/g;" < test_bram_tb.v > ${id}_tb.v + ../../../yosys -ql ${id}_syn.log -p "synth_ice40" -o ${id}_syn.v ${id}.v + # iverilog -s bram_tb -o ${id}_tb ${id}_syn.v ${id}_tb.v /opt/lscc/iCEcube2.2014.08/verilog/sb_ice_syn.v + iverilog -s bram_tb -o ${id}_tb ${id}_syn.v ${id}_tb.v ../cells_sim.v + ./${id}_tb > ${id}_tb.txt + if grep -H ERROR ${id}_tb.txt; then false; fi +done; done +echo OK + diff --git a/techlibs/ice40/tests/test_bram.v b/techlibs/ice40/tests/test_bram.v new file mode 100644 index 000000000..320735d07 --- /dev/null +++ b/techlibs/ice40/tests/test_bram.v @@ -0,0 +1,24 @@ +module bram #( + parameter ABITS = 8, DBITS = 8, + parameter INIT_ADDR = 0, INIT_DATA = 0 +) ( + input clk, + + input [ABITS-1:0] WR_ADDR, + input [DBITS-1:0] WR_DATA, + input WR_EN, + + input [ABITS-1:0] RD_ADDR, + output reg [DBITS-1:0] RD_DATA +); + reg [DBITS-1:0] memory [0:2**ABITS-1]; + + initial begin + memory[INIT_ADDR] <= INIT_DATA; + end + + always @(posedge clk) begin + if (WR_EN) memory[WR_ADDR] <= WR_DATA; + RD_DATA <= memory[RD_ADDR]; + end +endmodule diff --git a/techlibs/ice40/tests/test_bram_tb.v b/techlibs/ice40/tests/test_bram_tb.v new file mode 100644 index 000000000..bdb8d4560 --- /dev/null +++ b/techlibs/ice40/tests/test_bram_tb.v @@ -0,0 +1,110 @@ +module bram_tb #( + parameter ABITS = 8, DBITS = 8, + parameter INIT_ADDR = 0, INIT_DATA = 0 +); + reg clk; + reg [ABITS-1:0] WR_ADDR; + reg [DBITS-1:0] WR_DATA; + reg WR_EN; + reg [ABITS-1:0] RD_ADDR; + wire [DBITS-1:0] RD_DATA; + + bram uut ( + .clk (clk ), + .WR_ADDR(WR_ADDR), + .WR_DATA(WR_DATA), + .WR_EN (WR_EN ), + .RD_ADDR(RD_ADDR), + .RD_DATA(RD_DATA) + ); + + reg [63:0] xorshift64_state = 64'd88172645463325252 ^ (ABITS << 24) ^ (DBITS << 16); + + task xorshift64_next; + begin + // see page 4 of Marsaglia, George (July 2003). "Xorshift RNGs". Journal of Statistical Software 8 (14). + xorshift64_state = xorshift64_state ^ (xorshift64_state << 13); + xorshift64_state = xorshift64_state ^ (xorshift64_state >> 7); + xorshift64_state = xorshift64_state ^ (xorshift64_state << 17); + end + endtask + + reg [ABITS-1:0] randaddr1; + reg [ABITS-1:0] randaddr2; + reg [ABITS-1:0] randaddr3; + + function [31:0] getaddr(input [3:0] n); + begin + case (n) + 0: getaddr = 0; + 1: getaddr = 2**ABITS-1; + 2: getaddr = 'b101 << (ABITS / 3); + 3: getaddr = 'b101 << (2*ABITS / 3); + 4: getaddr = 'b11011 << (ABITS / 4); + 5: getaddr = 'b11011 << (2*ABITS / 4); + 6: getaddr = 'b11011 << (3*ABITS / 4); + 7: getaddr = randaddr1; + 8: getaddr = randaddr2; + 9: getaddr = randaddr3; + default: begin + getaddr = 1 << (2*n-16); + if (!getaddr) getaddr = xorshift64_state; + end + endcase + end + endfunction + + reg [DBITS-1:0] memory [0:2**ABITS-1]; + reg [DBITS-1:0] expected_rd, expected_rd_masked; + + event error; + integer i, j; + + initial begin + // $dumpfile("testbench.vcd"); + // $dumpvars(0, bram_tb); + + memory[INIT_ADDR] <= INIT_DATA; + + xorshift64_next; + xorshift64_next; + xorshift64_next; + xorshift64_next; + + randaddr1 = xorshift64_state; + xorshift64_next; + + randaddr2 = xorshift64_state; + xorshift64_next; + + randaddr3 = xorshift64_state; + xorshift64_next; + + clk <= 0; + for (i = 0; i < 512; i = i+1) begin + WR_DATA = xorshift64_state; + xorshift64_next; + + WR_ADDR = getaddr(i < 256 ? i[7:4] : xorshift64_state[63:60]); + xorshift64_next; + + RD_ADDR = i == 0 ? INIT_ADDR : getaddr(i < 256 ? i[3:0] : xorshift64_state[59:56]); + WR_EN = xorshift64_state[55] && ((WR_ADDR & 'hff) != (RD_ADDR & 'hff)); + xorshift64_next; + + #1; clk <= 1; + #1; clk <= 0; + + expected_rd = memory[RD_ADDR]; + if (WR_EN) memory[WR_ADDR] = WR_DATA; + + for (j = 0; j < DBITS; j = j+1) + expected_rd_masked[j] = expected_rd[j] !== 1'bx ? expected_rd[j] : RD_DATA[j]; + + $display("#OUT# %3d | WA=%x WD=%x WE=%x | RA=%x RD=%x (%x) | %s", + i, WR_ADDR, WR_DATA, WR_EN, RD_ADDR, RD_DATA, expected_rd, + expected_rd_masked === RD_DATA ? "ok" : "ERROR"); + if (expected_rd_masked !== RD_DATA) begin -> error; end + end + end +endmodule diff --git a/techlibs/ice40/tests/test_ffs.sh b/techlibs/ice40/tests/test_ffs.sh new file mode 100644 index 000000000..ff79ec534 --- /dev/null +++ b/techlibs/ice40/tests/test_ffs.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -ex +for CLKPOL in 0 1; do +for ENABLE_EN in 0 1; do +for RESET_EN in 0 1; do +for RESET_VAL in 0 1; do +for RESET_SYN in 0 1; do + pf="test_ffs_${CLKPOL}${ENABLE_EN}${RESET_EN}${RESET_VAL}${RESET_SYN}" + sed -e "s/CLKPOL = 0/CLKPOL = ${CLKPOL}/;" -e "s/ENABLE_EN = 0/ENABLE_EN = ${ENABLE_EN}/;" \ + -e "s/RESET_EN = 0/RESET_EN = ${RESET_EN}/;" -e "s/RESET_VAL = 0/RESET_VAL = ${RESET_VAL}/;" \ + -e "s/RESET_SYN = 0/RESET_SYN = ${RESET_SYN}/;" test_ffs.v > ${pf}_gold.v + ../../../yosys -o ${pf}_gate.v -p "synth_ice40" ${pf}_gold.v + ../../../yosys -p "proc; opt; test_autotb ${pf}_tb.v" ${pf}_gold.v + iverilog -s testbench -o ${pf}_gold ${pf}_gold.v ${pf}_tb.v + iverilog -s testbench -o ${pf}_gate ${pf}_gate.v ${pf}_tb.v ../cells_sim.v + ./${pf}_gold > ${pf}_gold.txt + ./${pf}_gate > ${pf}_gate.txt + cmp ${pf}_gold.txt ${pf}_gate.txt +done; done; done; done; done +echo OK. diff --git a/techlibs/ice40/tests/test_ffs.v b/techlibs/ice40/tests/test_ffs.v new file mode 100644 index 000000000..1f6883f3c --- /dev/null +++ b/techlibs/ice40/tests/test_ffs.v @@ -0,0 +1,42 @@ +module test(D, C, E, R, Q); + parameter [0:0] CLKPOL = 0; + parameter [0:0] ENABLE_EN = 0; + parameter [0:0] RESET_EN = 0; + parameter [0:0] RESET_VAL = 0; + parameter [0:0] RESET_SYN = 0; + + (* gentb_clock *) + input D, C, E, R; + + output Q; + + wire gated_reset = R & RESET_EN; + wire gated_enable = E | ~ENABLE_EN; + reg posedge_q, negedge_q, posedge_sq, negedge_sq; + + always @(posedge C, posedge gated_reset) + if (gated_reset) + posedge_q <= RESET_VAL; + else if (gated_enable) + posedge_q <= D; + + always @(negedge C, posedge gated_reset) + if (gated_reset) + negedge_q <= RESET_VAL; + else if (gated_enable) + negedge_q <= D; + + always @(posedge C) + if (gated_reset) + posedge_sq <= RESET_VAL; + else if (gated_enable) + posedge_sq <= D; + + always @(negedge C) + if (gated_reset) + negedge_sq <= RESET_VAL; + else if (gated_enable) + negedge_sq <= D; + + assign Q = RESET_SYN ? (CLKPOL ? posedge_sq : negedge_sq) : (CLKPOL ? posedge_q : negedge_q); +endmodule diff --git a/techlibs/xilinx/.gitignore b/techlibs/xilinx/.gitignore new file mode 100644 index 000000000..d127107db --- /dev/null +++ b/techlibs/xilinx/.gitignore @@ -0,0 +1,2 @@ +brams_init.mk +brams_init_*.vh diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc index 9af7b58f3..5f09ffb02 100644 --- a/techlibs/xilinx/Makefile.inc +++ b/techlibs/xilinx/Makefile.inc @@ -1,9 +1,37 @@ OBJS += techlibs/xilinx/synth_xilinx.o +GENFILES += techlibs/xilinx/brams_init_36.vh +GENFILES += techlibs/xilinx/brams_init_32.vh +GENFILES += techlibs/xilinx/brams_init_18.vh +GENFILES += techlibs/xilinx/brams_init_16.vh + +EXTRA_OBJS += techlibs/xilinx/brams_init.mk +.SECONDARY: techlibs/xilinx/brams_init.mk + +techlibs/xilinx/brams_init.mk: techlibs/xilinx/brams_init.py + $(Q) mkdir -p techlibs/xilinx + $(P) python3 $< + $(Q) touch $@ + +techlibs/xilinx/brams_init_36.vh: techlibs/xilinx/brams_init.mk +techlibs/xilinx/brams_init_32.vh: techlibs/xilinx/brams_init.mk +techlibs/xilinx/brams_init_18.vh: techlibs/xilinx/brams_init.mk +techlibs/xilinx/brams_init_16.vh: techlibs/xilinx/brams_init.mk + $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_sim.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_xtra.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/brams.txt)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/brams_map.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/brams_bb.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/drams.txt)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/drams_map.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/drams_bb.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/arith_map.v)) +$(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_36.vh)) +$(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_32.vh)) +$(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_18.vh)) +$(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_16.vh)) + diff --git a/techlibs/xilinx/arith_map.v b/techlibs/xilinx/arith_map.v index a154f7740..03719659b 100644 --- a/techlibs/xilinx/arith_map.v +++ b/techlibs/xilinx/arith_map.v @@ -2,11 +2,11 @@ * 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 diff --git a/techlibs/xilinx/brams.txt b/techlibs/xilinx/brams.txt index 84c114578..f1161114e 100644 --- a/techlibs/xilinx/brams.txt +++ b/techlibs/xilinx/brams.txt @@ -1,29 +1,32 @@ bram $__XILINX_RAMB36_SDP + init 1 abits 9 dbits 72 groups 2 ports 1 1 wrmode 0 1 - enable 0 8 + enable 1 8 transp 0 0 clocks 2 3 clkpol 2 3 endbram bram $__XILINX_RAMB18_SDP + init 1 abits 9 dbits 36 groups 2 ports 1 1 wrmode 0 1 - enable 0 4 + enable 1 4 transp 0 0 clocks 2 3 clkpol 2 3 endbram bram $__XILINX_RAMB36_TDP + init 1 abits 10 @a10d36 dbits 36 @a10d36 abits 11 @a11d18 @@ -39,15 +42,16 @@ bram $__XILINX_RAMB36_TDP groups 2 ports 1 1 wrmode 0 1 - enable 0 4 @a10d36 - enable 0 2 @a11d18 - enable 0 1 @a12d9 @a13d4 @a14d2 @a15d1 + enable 1 4 @a10d36 + enable 1 2 @a11d18 + enable 1 1 @a12d9 @a13d4 @a14d2 @a15d1 transp 0 0 clocks 2 3 clkpol 2 3 endbram bram $__XILINX_RAMB18_TDP + init 1 abits 10 @a10d18 dbits 18 @a10d18 abits 11 @a11d9 @@ -61,8 +65,8 @@ bram $__XILINX_RAMB18_TDP groups 2 ports 1 1 wrmode 0 1 - enable 0 2 @a10d18 - enable 0 1 @a11d9 @a12d4 @a13d2 @a14d1 + enable 1 2 @a10d18 + enable 1 1 @a11d9 @a12d4 @a13d2 @a14d1 transp 0 0 clocks 2 3 clkpol 2 3 @@ -98,4 +102,4 @@ match $__XILINX_RAMB18_TDP shuffle_enable B make_transp endmatch - + diff --git a/techlibs/xilinx/brams_bb.v b/techlibs/xilinx/brams_bb.v new file mode 100644 index 000000000..a682ba4a7 --- /dev/null +++ b/techlibs/xilinx/brams_bb.v @@ -0,0 +1,319 @@ +module RAMB18E1 ( + input CLKARDCLK, + input CLKBWRCLK, + input ENARDEN, + input ENBWREN, + input REGCEAREGCE, + input REGCEB, + input RSTRAMARSTRAM, + input RSTRAMB, + input RSTREGARSTREG, + input RSTREGB, + + input [13:0] ADDRARDADDR, + input [13:0] ADDRBWRADDR, + input [15:0] DIADI, + input [15:0] DIBDI, + input [1:0] DIPADIP, + input [1:0] DIPBDIP, + input [1:0] WEA, + input [3:0] WEBWE, + + output [15:0] DOADO, + output [15:0] DOBDO, + output [1:0] DOPADOP, + output [1:0] DOPBDOP +); + parameter INITP_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_02 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_03 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_04 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_05 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_06 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_07 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + + parameter INIT_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_02 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_03 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_04 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_05 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_06 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_07 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_08 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_09 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_10 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_12 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_13 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_14 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_15 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_16 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_17 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_18 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_19 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_20 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_21 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_22 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_23 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_24 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_25 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_26 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_27 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_28 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_29 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_30 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_31 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_32 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_33 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_34 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_35 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_36 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_37 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_38 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_39 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + + parameter IS_CLKARDCLK_INVERTED = 1'b0; + parameter IS_CLKBWRCLK_INVERTED = 1'b0; + parameter IS_ENARDEN_INVERTED = 1'b0; + parameter IS_ENBWREN_INVERTED = 1'b0; + parameter IS_RSTRAMARSTRAM_INVERTED = 1'b0; + parameter IS_RSTRAMB_INVERTED = 1'b0; + parameter IS_RSTREGARSTREG_INVERTED = 1'b0; + parameter IS_RSTREGB_INVERTED = 1'b0; + + parameter RAM_MODE = "TDP"; + parameter integer DOA_REG = 0; + parameter integer DOB_REG = 0; + + parameter integer READ_WIDTH_A = 0; + parameter integer READ_WIDTH_B = 0; + parameter integer WRITE_WIDTH_A = 0; + parameter integer WRITE_WIDTH_B = 0; + + parameter WRITE_MODE_A = "WRITE_FIRST"; + parameter WRITE_MODE_B = "WRITE_FIRST"; + + parameter SIM_DEVICE = "VIRTEX6"; +endmodule + +module RAMB36E1 ( + input CLKARDCLK, + input CLKBWRCLK, + input ENARDEN, + input ENBWREN, + input REGCEAREGCE, + input REGCEB, + input RSTRAMARSTRAM, + input RSTRAMB, + input RSTREGARSTREG, + input RSTREGB, + + input [15:0] ADDRARDADDR, + input [15:0] ADDRBWRADDR, + input [31:0] DIADI, + input [31:0] DIBDI, + input [3:0] DIPADIP, + input [3:0] DIPBDIP, + input [3:0] WEA, + input [7:0] WEBWE, + + output [31:0] DOADO, + output [31:0] DOBDO, + output [3:0] DOPADOP, + output [3:0] DOPBDOP +); + parameter INITP_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_02 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_03 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_04 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_05 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_06 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_07 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_08 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_09 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_0A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_0B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_0C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_0D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_0E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_0F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + + parameter INIT_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_02 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_03 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_04 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_05 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_06 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_07 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_08 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_09 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_0F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_10 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_12 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_13 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_14 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_15 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_16 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_17 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_18 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_19 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_1F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_20 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_21 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_22 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_23 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_24 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_25 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_26 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_27 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_28 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_29 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_2F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_30 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_31 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_32 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_33 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_34 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_35 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_36 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_37 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_38 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_39 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_3F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_40 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_41 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_42 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_43 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_44 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_45 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_46 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_47 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_48 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_49 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_4A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_4B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_4C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_4D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_4E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_4F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_50 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_51 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_52 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_53 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_54 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_55 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_56 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_57 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_58 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_59 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_5A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_5B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_5C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_5D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_5E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_5F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_60 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_61 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_62 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_63 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_64 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_65 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_66 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_67 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_68 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_69 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_6A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_6B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_6C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_6D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_6E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_6F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_70 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_71 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_72 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_73 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_74 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_75 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_76 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_77 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_78 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_79 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_7A = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_7B = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_7C = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_7D = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_7E = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INIT_7F = 256'h0000000000000000000000000000000000000000000000000000000000000000; + + parameter IS_CLKARDCLK_INVERTED = 1'b0; + parameter IS_CLKBWRCLK_INVERTED = 1'b0; + parameter IS_ENARDEN_INVERTED = 1'b0; + parameter IS_ENBWREN_INVERTED = 1'b0; + parameter IS_RSTRAMARSTRAM_INVERTED = 1'b0; + parameter IS_RSTRAMB_INVERTED = 1'b0; + parameter IS_RSTREGARSTREG_INVERTED = 1'b0; + parameter IS_RSTREGB_INVERTED = 1'b0; + + parameter RAM_MODE = "TDP"; + parameter integer DOA_REG = 0; + parameter integer DOB_REG = 0; + + parameter integer READ_WIDTH_A = 0; + parameter integer READ_WIDTH_B = 0; + parameter integer WRITE_WIDTH_A = 0; + parameter integer WRITE_WIDTH_B = 0; + + parameter WRITE_MODE_A = "WRITE_FIRST"; + parameter WRITE_MODE_B = "WRITE_FIRST"; + + parameter SIM_DEVICE = "VIRTEX6"; +endmodule diff --git a/techlibs/xilinx/brams_init.py b/techlibs/xilinx/brams_init.py new file mode 100644 index 000000000..e787b1f76 --- /dev/null +++ b/techlibs/xilinx/brams_init.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +with open("techlibs/xilinx/brams_init_18.vh", "w") as f: + for i in range(8): + init_snippets = ["INIT[%3d*9+8]" % (k+256*i,) for k in range(255, -1, -1)] + for k in range(4, 256, 4): + init_snippets[k] = "\n " + init_snippets[k] + print(".INITP_%02X({%s})," % (i, ", ".join(init_snippets)), file=f) + for i in range(64): + init_snippets = ["INIT[%3d*9 +: 8]" % (k+32*i,) for k in range(31, -1, -1)] + for k in range(4, 32, 4): + init_snippets[k] = "\n " + init_snippets[k] + print(".INIT_%02X({%s})," % (i, ", ".join(init_snippets)), file=f) + +with open("techlibs/xilinx/brams_init_36.vh", "w") as f: + for i in range(16): + init_snippets = ["INIT[%3d*9+8]" % (k+256*i,) for k in range(255, -1, -1)] + for k in range(4, 256, 4): + init_snippets[k] = "\n " + init_snippets[k] + print(".INITP_%02X({%s})," % (i, ", ".join(init_snippets)), file=f) + for i in range(128): + init_snippets = ["INIT[%3d*9 +: 8]" % (k+32*i,) for k in range(31, -1, -1)] + for k in range(4, 32, 4): + init_snippets[k] = "\n " + init_snippets[k] + print(".INIT_%02X({%s})," % (i, ", ".join(init_snippets)), file=f) + +with open("techlibs/xilinx/brams_init_16.vh", "w") as f: + for i in range(64): + print(".INIT_%02X(INIT[%3d*256 +: 256])," % (i, i), file=f) + +with open("techlibs/xilinx/brams_init_32.vh", "w") as f: + for i in range(128): + print(".INIT_%02X(INIT[%3d*256 +: 256])," % (i, i), file=f) + diff --git a/techlibs/xilinx/brams_map.v b/techlibs/xilinx/brams_map.v index 2e9bba9a9..7ea49158d 100644 --- a/techlibs/xilinx/brams_map.v +++ b/techlibs/xilinx/brams_map.v @@ -1,12 +1,14 @@ -module \$__XILINX_RAMB36_SDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); +module \$__XILINX_RAMB36_SDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); parameter CLKPOL2 = 1; parameter CLKPOL3 = 1; + parameter [36863:0] INIT = 36864'bx; input CLK2; input CLK3; input [8:0] A1ADDR; output [71:0] A1DATA; + input A1EN; input [8:0] B1ADDR; input [71:0] B1DATA; @@ -32,6 +34,7 @@ module \$__XILINX_RAMB36_SDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); .WRITE_MODE_B("READ_FIRST"), .IS_CLKARDCLK_INVERTED(!CLKPOL2), .IS_CLKBWRCLK_INVERTED(!CLKPOL3), + `include "brams_init_36.vh" .SIM_DEVICE("7SERIES") ) _TECHMAP_REPLACE_ ( .DOBDO(DO[63:32]), @@ -45,7 +48,7 @@ module \$__XILINX_RAMB36_SDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); .ADDRARDADDR(A1ADDR_16), .CLKARDCLK(CLK2), - .ENARDEN(|1), + .ENARDEN(A1EN), .REGCEAREGCE(|1), .RSTRAMARSTRAM(|0), .RSTREGARSTREG(|0), @@ -63,15 +66,17 @@ endmodule // ------------------------------------------------------------------------ -module \$__XILINX_RAMB18_SDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); +module \$__XILINX_RAMB18_SDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); parameter CLKPOL2 = 1; parameter CLKPOL3 = 1; + parameter [18431:0] INIT = 18432'bx; input CLK2; input CLK3; input [8:0] A1ADDR; output [35:0] A1DATA; + input A1EN; input [8:0] B1ADDR; input [35:0] B1DATA; @@ -94,6 +99,7 @@ module \$__XILINX_RAMB18_SDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); .WRITE_MODE_B("READ_FIRST"), .IS_CLKARDCLK_INVERTED(!CLKPOL2), .IS_CLKBWRCLK_INVERTED(!CLKPOL3), + `include "brams_init_18.vh" .SIM_DEVICE("7SERIES") ) _TECHMAP_REPLACE_ ( .DOBDO(DO[31:16]), @@ -107,7 +113,7 @@ module \$__XILINX_RAMB18_SDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); .ADDRARDADDR(A1ADDR_14), .CLKARDCLK(CLK2), - .ENARDEN(|1), + .ENARDEN(A1EN), .REGCEAREGCE(|1), .RSTRAMARSTRAM(|0), .RSTREGARSTREG(|0), @@ -125,19 +131,21 @@ endmodule // ------------------------------------------------------------------------ -module \$__XILINX_RAMB36_TDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); +module \$__XILINX_RAMB36_TDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); parameter CFG_ABITS = 10; parameter CFG_DBITS = 36; parameter CFG_ENABLE_B = 4; parameter CLKPOL2 = 1; parameter CLKPOL3 = 1; + parameter [36863:0] INIT = 36864'bx; input CLK2; input CLK3; input [CFG_ABITS-1:0] A1ADDR; output [CFG_DBITS-1:0] A1DATA; + input A1EN; input [CFG_ABITS-1:0] B1ADDR; input [CFG_DBITS-1:0] B1DATA; @@ -156,59 +164,102 @@ module \$__XILINX_RAMB36_TDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); assign A1DATA = { DOP[3], DO[31:24], DOP[2], DO[23:16], DOP[1], DO[15: 8], DOP[0], DO[ 7: 0] }; assign { DIP[3], DI[31:24], DIP[2], DI[23:16], DIP[1], DI[15: 8], DIP[0], DI[ 7: 0] } = B1DATA; - RAMB36E1 #( - .RAM_MODE("TDP"), - .READ_WIDTH_A(CFG_DBITS), - .READ_WIDTH_B(CFG_DBITS), - .WRITE_WIDTH_A(CFG_DBITS), - .WRITE_WIDTH_B(CFG_DBITS), - .WRITE_MODE_A("READ_FIRST"), - .WRITE_MODE_B("READ_FIRST"), - .IS_CLKARDCLK_INVERTED(!CLKPOL2), - .IS_CLKBWRCLK_INVERTED(!CLKPOL3), - .SIM_DEVICE("7SERIES") - ) _TECHMAP_REPLACE_ ( - .DIADI(32'd0), - .DIPADIP(4'd0), - .DOADO(DO[31:0]), - .DOPADOP(DOP[3:0]), - .ADDRARDADDR(A1ADDR_16), - .CLKARDCLK(CLK2), - .ENARDEN(|1), - .REGCEAREGCE(|1), - .RSTRAMARSTRAM(|0), - .RSTREGARSTREG(|0), - .WEA(4'b0), - - .DIBDI(DI), - .DIPBDIP(DIP), - .DOBDO(DOBDO), - .DOPBDOP(DOPBDOP), - .ADDRBWRADDR(B1ADDR_16), - .CLKBWRCLK(CLK3), - .ENBWREN(|1), - .REGCEB(|0), - .RSTRAMB(|0), - .RSTREGB(|0), - .WEBWE(B1EN_8) - ); + generate if (CFG_DBITS > 8) begin + RAMB36E1 #( + .RAM_MODE("TDP"), + .READ_WIDTH_A(CFG_DBITS), + .READ_WIDTH_B(CFG_DBITS), + .WRITE_WIDTH_A(CFG_DBITS), + .WRITE_WIDTH_B(CFG_DBITS), + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + .IS_CLKARDCLK_INVERTED(!CLKPOL2), + .IS_CLKBWRCLK_INVERTED(!CLKPOL3), + `include "brams_init_36.vh" + .SIM_DEVICE("7SERIES") + ) _TECHMAP_REPLACE_ ( + .DIADI(32'd0), + .DIPADIP(4'd0), + .DOADO(DO[31:0]), + .DOPADOP(DOP[3:0]), + .ADDRARDADDR(A1ADDR_16), + .CLKARDCLK(CLK2), + .ENARDEN(A1EN), + .REGCEAREGCE(|1), + .RSTRAMARSTRAM(|0), + .RSTREGARSTREG(|0), + .WEA(4'b0), + + .DIBDI(DI), + .DIPBDIP(DIP), + .DOBDO(DOBDO), + .DOPBDOP(DOPBDOP), + .ADDRBWRADDR(B1ADDR_16), + .CLKBWRCLK(CLK3), + .ENBWREN(|1), + .REGCEB(|0), + .RSTRAMB(|0), + .RSTREGB(|0), + .WEBWE(B1EN_8) + ); + end else begin + RAMB36E1 #( + .RAM_MODE("TDP"), + .READ_WIDTH_A(CFG_DBITS), + .READ_WIDTH_B(CFG_DBITS), + .WRITE_WIDTH_A(CFG_DBITS), + .WRITE_WIDTH_B(CFG_DBITS), + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + .IS_CLKARDCLK_INVERTED(!CLKPOL2), + .IS_CLKBWRCLK_INVERTED(!CLKPOL3), + `include "brams_init_32.vh" + .SIM_DEVICE("7SERIES") + ) _TECHMAP_REPLACE_ ( + .DIADI(32'd0), + .DIPADIP(4'd0), + .DOADO(DO[31:0]), + .DOPADOP(DOP[3:0]), + .ADDRARDADDR(A1ADDR_16), + .CLKARDCLK(CLK2), + .ENARDEN(A1EN), + .REGCEAREGCE(|1), + .RSTRAMARSTRAM(|0), + .RSTREGARSTREG(|0), + .WEA(4'b0), + + .DIBDI(DI), + .DIPBDIP(DIP), + .DOBDO(DOBDO), + .DOPBDOP(DOPBDOP), + .ADDRBWRADDR(B1ADDR_16), + .CLKBWRCLK(CLK3), + .ENBWREN(|1), + .REGCEB(|0), + .RSTRAMB(|0), + .RSTREGB(|0), + .WEBWE(B1EN_8) + ); + end endgenerate endmodule // ------------------------------------------------------------------------ -module \$__XILINX_RAMB18_TDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); +module \$__XILINX_RAMB18_TDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); parameter CFG_ABITS = 10; parameter CFG_DBITS = 18; parameter CFG_ENABLE_B = 2; parameter CLKPOL2 = 1; parameter CLKPOL3 = 1; + parameter [18431:0] INIT = 18432'bx; input CLK2; input CLK3; input [CFG_ABITS-1:0] A1ADDR; output [CFG_DBITS-1:0] A1DATA; + input A1EN; input [CFG_ABITS-1:0] B1ADDR; input [CFG_DBITS-1:0] B1DATA; @@ -227,41 +278,82 @@ module \$__XILINX_RAMB18_TDP (CLK2, CLK3, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); assign A1DATA = { DOP[1], DO[15: 8], DOP[0], DO[ 7: 0] }; assign { DIP[1], DI[15: 8], DIP[0], DI[ 7: 0] } = B1DATA; - RAMB18E1 #( - .RAM_MODE("TDP"), - .READ_WIDTH_A(CFG_DBITS), - .READ_WIDTH_B(CFG_DBITS), - .WRITE_WIDTH_A(CFG_DBITS), - .WRITE_WIDTH_B(CFG_DBITS), - .WRITE_MODE_A("READ_FIRST"), - .WRITE_MODE_B("READ_FIRST"), - .IS_CLKARDCLK_INVERTED(!CLKPOL2), - .IS_CLKBWRCLK_INVERTED(!CLKPOL3), - .SIM_DEVICE("7SERIES") - ) _TECHMAP_REPLACE_ ( - .DIADI(16'b0), - .DIPADIP(2'b0), - .DOADO(DO), - .DOPADOP(DOP), - .ADDRARDADDR(A1ADDR_14), - .CLKARDCLK(CLK2), - .ENARDEN(|1), - .REGCEAREGCE(|1), - .RSTRAMARSTRAM(|0), - .RSTREGARSTREG(|0), - .WEA(2'b0), - - .DIBDI(DI), - .DIPBDIP(DIP), - .DOBDO(DOBDO), - .DOPBDOP(DOPBDOP), - .ADDRBWRADDR(B1ADDR_14), - .CLKBWRCLK(CLK3), - .ENBWREN(|1), - .REGCEB(|0), - .RSTRAMB(|0), - .RSTREGB(|0), - .WEBWE(B1EN_4) - ); + generate if (CFG_DBITS > 8) begin + RAMB18E1 #( + .RAM_MODE("TDP"), + .READ_WIDTH_A(CFG_DBITS), + .READ_WIDTH_B(CFG_DBITS), + .WRITE_WIDTH_A(CFG_DBITS), + .WRITE_WIDTH_B(CFG_DBITS), + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + .IS_CLKARDCLK_INVERTED(!CLKPOL2), + .IS_CLKBWRCLK_INVERTED(!CLKPOL3), + `include "brams_init_18.vh" + .SIM_DEVICE("7SERIES") + ) _TECHMAP_REPLACE_ ( + .DIADI(16'b0), + .DIPADIP(2'b0), + .DOADO(DO), + .DOPADOP(DOP), + .ADDRARDADDR(A1ADDR_14), + .CLKARDCLK(CLK2), + .ENARDEN(A1EN), + .REGCEAREGCE(|1), + .RSTRAMARSTRAM(|0), + .RSTREGARSTREG(|0), + .WEA(2'b0), + + .DIBDI(DI), + .DIPBDIP(DIP), + .DOBDO(DOBDO), + .DOPBDOP(DOPBDOP), + .ADDRBWRADDR(B1ADDR_14), + .CLKBWRCLK(CLK3), + .ENBWREN(|1), + .REGCEB(|0), + .RSTRAMB(|0), + .RSTREGB(|0), + .WEBWE(B1EN_4) + ); + end else begin + RAMB18E1 #( + .RAM_MODE("TDP"), + .READ_WIDTH_A(CFG_DBITS), + .READ_WIDTH_B(CFG_DBITS), + .WRITE_WIDTH_A(CFG_DBITS), + .WRITE_WIDTH_B(CFG_DBITS), + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + .IS_CLKARDCLK_INVERTED(!CLKPOL2), + .IS_CLKBWRCLK_INVERTED(!CLKPOL3), + `include "brams_init_16.vh" + .SIM_DEVICE("7SERIES") + ) _TECHMAP_REPLACE_ ( + .DIADI(16'b0), + .DIPADIP(2'b0), + .DOADO(DO), + .DOPADOP(DOP), + .ADDRARDADDR(A1ADDR_14), + .CLKARDCLK(CLK2), + .ENARDEN(A1EN), + .REGCEAREGCE(|1), + .RSTRAMARSTRAM(|0), + .RSTREGARSTREG(|0), + .WEA(2'b0), + + .DIBDI(DI), + .DIPBDIP(DIP), + .DOBDO(DOBDO), + .DOPBDOP(DOPBDOP), + .ADDRBWRADDR(B1ADDR_14), + .CLKBWRCLK(CLK3), + .ENBWREN(|1), + .REGCEB(|0), + .RSTRAMB(|0), + .RSTREGB(|0), + .WEBWE(B1EN_4) + ); + end endgenerate endmodule diff --git a/techlibs/xilinx/cells_xtra.sh b/techlibs/xilinx/cells_xtra.sh new file mode 100644 index 000000000..c7ad16043 --- /dev/null +++ b/techlibs/xilinx/cells_xtra.sh @@ -0,0 +1,145 @@ +#!/bin/bash + +set -e +libdir="/opt/Xilinx/Vivado/2015.4/data/verilog/src" + +function xtract_cell_decl() +{ + for dir in $libdir/xeclib $libdir/retarget; do + [ -f $dir/$1.v ] || continue + egrep '^\s*((end)?module|parameter|input|output|(end)?function|(end)?task)' $dir/$1.v | + sed -re '/UNPLACED/ d; /^\s*function/,/endfunction/ d; /^\s*task/,/endtask/ d; + s,//.*,,; s/#?\(.*/(...);/; s/^(input|output|parameter)/ \1/; + s/\s+$//; s/,$/;/; /input|output|parameter/ s/[^;]$/&;/; s/\s+/ /g; + s/^ ((end)?module)/\1/; s/^ / /; /module.*_bb/,/endmodule/ d;' + echo; return + done + echo "Can't find $1." + exit 1 +} + +{ + echo "// Created by cells_xtra.sh from Xilinx models" + echo + + # Design elements types listed in Xilinx UG953 + xtract_cell_decl BSCANE2 + # xtract_cell_decl BUFG + xtract_cell_decl BUFGCE + xtract_cell_decl BUFGCE_1 + xtract_cell_decl BUFGCTRL + xtract_cell_decl BUFGMUX + xtract_cell_decl BUFGMUX_1 + xtract_cell_decl BUFGMUX_CTRL + xtract_cell_decl BUFH + xtract_cell_decl BUFHCE + xtract_cell_decl BUFIO + xtract_cell_decl BUFMR + xtract_cell_decl BUFMRCE + xtract_cell_decl BUFR + xtract_cell_decl CAPTUREE2 + # xtract_cell_decl CARRY4 + xtract_cell_decl CFGLUT5 + xtract_cell_decl DCIRESET + xtract_cell_decl DNA_PORT + xtract_cell_decl DSP48E1 + xtract_cell_decl EFUSE_USR + # xtract_cell_decl FDCE + # xtract_cell_decl FDPE + # xtract_cell_decl FDRE + # xtract_cell_decl FDSE + xtract_cell_decl FIFO18E1 + xtract_cell_decl FIFO36E1 + xtract_cell_decl FRAME_ECCE2 + xtract_cell_decl GTHE2_CHANNEL + xtract_cell_decl GTHE2_COMMON + xtract_cell_decl GTPE2_CHANNEL + xtract_cell_decl GTPE2_COMMON + xtract_cell_decl GTXE2_CHANNEL + xtract_cell_decl GTXE2_COMMON + # xtract_cell_decl IBUF + xtract_cell_decl IBUF_IBUFDISABLE + xtract_cell_decl IBUF_INTERMDISABLE + xtract_cell_decl IBUFDS + xtract_cell_decl IBUFDS_DIFF_OUT + xtract_cell_decl IBUFDS_DIFF_OUT_IBUFDISABLE + xtract_cell_decl IBUFDS_DIFF_OUT_INTERMDISABLE + xtract_cell_decl IBUFDS_GTE2 + xtract_cell_decl IBUFDS_IBUFDISABLE + xtract_cell_decl IBUFDS_INTERMDISABLE + xtract_cell_decl ICAPE2 + xtract_cell_decl IDDR + xtract_cell_decl IDDR_2CLK + xtract_cell_decl IDELAYCTRL + xtract_cell_decl IDELAYE2 + xtract_cell_decl IN_FIFO + xtract_cell_decl IOBUF + xtract_cell_decl IOBUF_DCIEN + xtract_cell_decl IOBUF_INTERMDISABLE + xtract_cell_decl IOBUFDS + xtract_cell_decl IOBUFDS_DCIEN + xtract_cell_decl IOBUFDS_DIFF_OUT + xtract_cell_decl IOBUFDS_DIFF_OUT_DCIEN + xtract_cell_decl IOBUFDS_DIFF_OUT_INTERMDISABLE + xtract_cell_decl ISERDESE2 + xtract_cell_decl KEEPER + xtract_cell_decl LDCE + xtract_cell_decl LDPE + # xtract_cell_decl LUT1 + # xtract_cell_decl LUT2 + # xtract_cell_decl LUT3 + # xtract_cell_decl LUT4 + # xtract_cell_decl LUT5 + # xtract_cell_decl LUT6 + xtract_cell_decl LUT6_2 + xtract_cell_decl MMCME2_ADV + xtract_cell_decl MMCME2_BASE + # xtract_cell_decl MUXF7 + # xtract_cell_decl MUXF8 + # xtract_cell_decl OBUF + xtract_cell_decl OBUFDS + xtract_cell_decl OBUFT + xtract_cell_decl OBUFTDS + xtract_cell_decl ODDR + xtract_cell_decl ODELAYE2 + xtract_cell_decl OSERDESE2 + xtract_cell_decl OUT_FIFO + xtract_cell_decl PHASER_IN + xtract_cell_decl PHASER_IN_PHY + xtract_cell_decl PHASER_OUT + xtract_cell_decl PHASER_OUT_PHY + xtract_cell_decl PHASER_REF + xtract_cell_decl PHY_CONTROL + xtract_cell_decl PLLE2_ADV + xtract_cell_decl PLLE2_BASE + xtract_cell_decl PULLDOWN + xtract_cell_decl PULLUP + # xtract_cell_decl RAM128X1D + xtract_cell_decl RAM128X1S + xtract_cell_decl RAM256X1S + xtract_cell_decl RAM32M + xtract_cell_decl RAM32X1D + xtract_cell_decl RAM32X1S + xtract_cell_decl RAM32X1S_1 + xtract_cell_decl RAM32X2S + xtract_cell_decl RAM64M + # xtract_cell_decl RAM64X1D + xtract_cell_decl RAM64X1S + xtract_cell_decl RAM64X1S_1 + xtract_cell_decl RAM64X2S + # xtract_cell_decl RAMB18E1 + # xtract_cell_decl RAMB36E1 + xtract_cell_decl ROM128X1 + xtract_cell_decl ROM256X1 + xtract_cell_decl ROM32X1 + xtract_cell_decl ROM64X1 + xtract_cell_decl SRL16E + xtract_cell_decl SRLC32E + xtract_cell_decl STARTUPE2 + xtract_cell_decl USR_ACCESSE2 + xtract_cell_decl XADC +} > cells_xtra.new + +mv cells_xtra.new cells_xtra.v +exit 0 + diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v new file mode 100644 index 000000000..a2dd01ad5 --- /dev/null +++ b/techlibs/xilinx/cells_xtra.v @@ -0,0 +1,3293 @@ +// Created by cells_xtra.sh from Xilinx models + +module BSCANE2 (...); + parameter DISABLE_JTAG = "FALSE"; + parameter integer JTAG_CHAIN = 1; + output CAPTURE; + output DRCK; + output RESET; + output RUNTEST; + output SEL; + output SHIFT; + output TCK; + output TDI; + output TMS; + output UPDATE; + input TDO; +endmodule + +module BUFGCE (...); + parameter CE_TYPE = "SYNC"; + parameter [0:0] IS_CE_INVERTED = 1'b0; + parameter [0:0] IS_I_INVERTED = 1'b0; + output O; + input CE; + input I; +endmodule + +module BUFGCE_1 (...); + output O; + input CE, I; +endmodule + +module BUFGCTRL (...); + output O; + input CE0; + input CE1; + input I0; + input I1; + input IGNORE0; + input IGNORE1; + input S0; + input S1; + parameter integer INIT_OUT = 0; + parameter PRESELECT_I0 = "FALSE"; + parameter PRESELECT_I1 = "FALSE"; + parameter [0:0] IS_CE0_INVERTED = 1'b0; + parameter [0:0] IS_CE1_INVERTED = 1'b0; + parameter [0:0] IS_I0_INVERTED = 1'b0; + parameter [0:0] IS_I1_INVERTED = 1'b0; + parameter [0:0] IS_IGNORE0_INVERTED = 1'b0; + parameter [0:0] IS_IGNORE1_INVERTED = 1'b0; + parameter [0:0] IS_S0_INVERTED = 1'b0; + parameter [0:0] IS_S1_INVERTED = 1'b0; +endmodule + +module BUFGMUX (...); + parameter CLK_SEL_TYPE = "SYNC"; + output O; + input I0, I1, S; +endmodule + +module BUFGMUX_1 (...); + parameter CLK_SEL_TYPE = "SYNC"; + output O; + input I0, I1, S; +endmodule + +module BUFGMUX_CTRL (...); + output O; + input I0; + input I1; + input S; +endmodule + +module BUFH (...); + output O; + input I; +endmodule + +module BUFHCE (...); + parameter CE_TYPE = "SYNC"; + parameter integer INIT_OUT = 0; + parameter [0:0] IS_CE_INVERTED = 1'b0; + output O; + input CE; + input I; +endmodule + +module BUFIO (...); + output O; + input I; +endmodule + +module BUFMR (...); + output O; + input I; +endmodule + +module BUFMRCE (...); + parameter CE_TYPE = "SYNC"; + parameter integer INIT_OUT = 0; + parameter [0:0] IS_CE_INVERTED = 1'b0; + output O; + input CE; + input I; +endmodule + +module BUFR (...); + output O; + input CE; + input CLR; + input I; + parameter BUFR_DIVIDE = "BYPASS"; + parameter SIM_DEVICE = "7SERIES"; +endmodule + +module CAPTUREE2 (...); + parameter ONESHOT = "TRUE"; + input CAP; + input CLK; +endmodule + +module CFGLUT5 (...); + parameter [31:0] INIT = 32'h00000000; + parameter [0:0] IS_CLK_INVERTED = 1'b0; + output CDO; + output O5; + output O6; + input I4, I3, I2, I1, I0; + input CDI, CE, CLK; +endmodule + +module DCIRESET (...); + output LOCKED; + input RST; +endmodule + +module DNA_PORT (...); + parameter [56:0] SIM_DNA_VALUE = 57'h0; + output DOUT; + input CLK, DIN, READ, SHIFT; +endmodule + +module DSP48E1 (...); + parameter integer ACASCREG = 1; + parameter integer ADREG = 1; + parameter integer ALUMODEREG = 1; + parameter integer AREG = 1; + parameter AUTORESET_PATDET = "NO_RESET"; + parameter A_INPUT = "DIRECT"; + parameter integer BCASCREG = 1; + parameter integer BREG = 1; + parameter B_INPUT = "DIRECT"; + parameter integer CARRYINREG = 1; + parameter integer CARRYINSELREG = 1; + parameter integer CREG = 1; + parameter integer DREG = 1; + parameter integer INMODEREG = 1; + parameter integer MREG = 1; + parameter integer OPMODEREG = 1; + parameter integer PREG = 1; + parameter SEL_MASK = "MASK"; + parameter SEL_PATTERN = "PATTERN"; + parameter USE_DPORT = "FALSE"; + parameter USE_MULT = "MULTIPLY"; + parameter USE_PATTERN_DETECT = "NO_PATDET"; + parameter USE_SIMD = "ONE48"; + parameter [47:0] MASK = 48'h3FFFFFFFFFFF; + parameter [47:0] PATTERN = 48'h000000000000; + parameter [3:0] IS_ALUMODE_INVERTED = 4'b0; + parameter [0:0] IS_CARRYIN_INVERTED = 1'b0; + parameter [0:0] IS_CLK_INVERTED = 1'b0; + parameter [4:0] IS_INMODE_INVERTED = 5'b0; + parameter [6:0] IS_OPMODE_INVERTED = 7'b0; + output [29:0] ACOUT; + output [17:0] BCOUT; + output CARRYCASCOUT; + output [3:0] CARRYOUT; + output MULTSIGNOUT; + output OVERFLOW; + output [47:0] P; + output PATTERNBDETECT; + output PATTERNDETECT; + output [47:0] PCOUT; + output UNDERFLOW; + input [29:0] A; + input [29:0] ACIN; + input [3:0] ALUMODE; + input [17:0] B; + input [17:0] BCIN; + input [47:0] C; + input CARRYCASCIN; + input CARRYIN; + input [2:0] CARRYINSEL; + input CEA1; + input CEA2; + input CEAD; + input CEALUMODE; + input CEB1; + input CEB2; + input CEC; + input CECARRYIN; + input CECTRL; + input CED; + input CEINMODE; + input CEM; + input CEP; + input CLK; + input [24:0] D; + input [4:0] INMODE; + input MULTSIGNIN; + input [6:0] OPMODE; + input [47:0] PCIN; + input RSTA; + input RSTALLCARRYIN; + input RSTALUMODE; + input RSTB; + input RSTC; + input RSTCTRL; + input RSTD; + input RSTINMODE; + input RSTM; + input RSTP; +endmodule + +module EFUSE_USR (...); + parameter [31:0] SIM_EFUSE_VALUE = 32'h00000000; + output [31:0] EFUSEUSR; +endmodule + +module FIFO18E1 (...); + parameter ALMOST_EMPTY_OFFSET = 13'h0080; + parameter ALMOST_FULL_OFFSET = 13'h0080; + parameter integer DATA_WIDTH = 4; + parameter integer DO_REG = 1; + parameter EN_SYN = "FALSE"; + parameter FIFO_MODE = "FIFO18"; + parameter FIRST_WORD_FALL_THROUGH = "FALSE"; + parameter INIT = 36'h0; + parameter SIM_DEVICE = "VIRTEX6"; + parameter SRVAL = 36'h0; + parameter IS_RDCLK_INVERTED = 1'b0; + parameter IS_RDEN_INVERTED = 1'b0; + parameter IS_RSTREG_INVERTED = 1'b0; + parameter IS_RST_INVERTED = 1'b0; + parameter IS_WRCLK_INVERTED = 1'b0; + parameter IS_WREN_INVERTED = 1'b0; + output ALMOSTEMPTY; + output ALMOSTFULL; + output [31:0] DO; + output [3:0] DOP; + output EMPTY; + output FULL; + output [11:0] RDCOUNT; + output RDERR; + output [11:0] WRCOUNT; + output WRERR; + input [31:0] DI; + input [3:0] DIP; + input RDCLK; + input RDEN; + input REGCE; + input RST; + input RSTREG; + input WRCLK; + input WREN; +endmodule + +module FIFO36E1 (...); + parameter ALMOST_EMPTY_OFFSET = 13'h0080; + parameter ALMOST_FULL_OFFSET = 13'h0080; + parameter integer DATA_WIDTH = 4; + parameter integer DO_REG = 1; + parameter EN_ECC_READ = "FALSE"; + parameter EN_ECC_WRITE = "FALSE"; + parameter EN_SYN = "FALSE"; + parameter FIFO_MODE = "FIFO36"; + parameter FIRST_WORD_FALL_THROUGH = "FALSE"; + parameter INIT = 72'h0; + parameter SIM_DEVICE = "VIRTEX6"; + parameter SRVAL = 72'h0; + parameter IS_RDCLK_INVERTED = 1'b0; + parameter IS_RDEN_INVERTED = 1'b0; + parameter IS_RSTREG_INVERTED = 1'b0; + parameter IS_RST_INVERTED = 1'b0; + parameter IS_WRCLK_INVERTED = 1'b0; + parameter IS_WREN_INVERTED = 1'b0; + output ALMOSTEMPTY; + output ALMOSTFULL; + output DBITERR; + output [63:0] DO; + output [7:0] DOP; + output [7:0] ECCPARITY; + output EMPTY; + output FULL; + output [12:0] RDCOUNT; + output RDERR; + output SBITERR; + output [12:0] WRCOUNT; + output WRERR; + input [63:0] DI; + input [7:0] DIP; + input INJECTDBITERR; + input INJECTSBITERR; + input RDCLK; + input RDEN; + input REGCE; + input RST; + input RSTREG; + input WRCLK; + input WREN; +endmodule + +module FRAME_ECCE2 (...); + parameter FARSRC = "EFAR"; + parameter FRAME_RBT_IN_FILENAME = "NONE"; + output CRCERROR; + output ECCERROR; + output ECCERRORSINGLE; + output SYNDROMEVALID; + output [12:0] SYNDROME; + output [25:0] FAR; + output [4:0] SYNBIT; + output [6:0] SYNWORD; +endmodule + +module GTHE2_CHANNEL (...); + parameter [0:0] ACJTAG_DEBUG_MODE = 1'b0; + parameter [0:0] ACJTAG_MODE = 1'b0; + parameter [0:0] ACJTAG_RESET = 1'b0; + parameter [19:0] ADAPT_CFG0 = 20'h00C10; + parameter ALIGN_COMMA_DOUBLE = "FALSE"; + parameter [9:0] ALIGN_COMMA_ENABLE = 10'b0001111111; + parameter integer ALIGN_COMMA_WORD = 1; + parameter ALIGN_MCOMMA_DET = "TRUE"; + parameter [9:0] ALIGN_MCOMMA_VALUE = 10'b1010000011; + parameter ALIGN_PCOMMA_DET = "TRUE"; + parameter [9:0] ALIGN_PCOMMA_VALUE = 10'b0101111100; + parameter [0:0] A_RXOSCALRESET = 1'b0; + parameter CBCC_DATA_SOURCE_SEL = "DECODED"; + parameter [41:0] CFOK_CFG = 42'h24800040E80; + parameter [5:0] CFOK_CFG2 = 6'b100000; + parameter [5:0] CFOK_CFG3 = 6'b100000; + parameter CHAN_BOND_KEEP_ALIGN = "FALSE"; + parameter integer CHAN_BOND_MAX_SKEW = 7; + parameter [9:0] CHAN_BOND_SEQ_1_1 = 10'b0101111100; + parameter [9:0] CHAN_BOND_SEQ_1_2 = 10'b0000000000; + parameter [9:0] CHAN_BOND_SEQ_1_3 = 10'b0000000000; + parameter [9:0] CHAN_BOND_SEQ_1_4 = 10'b0000000000; + parameter [3:0] CHAN_BOND_SEQ_1_ENABLE = 4'b1111; + parameter [9:0] CHAN_BOND_SEQ_2_1 = 10'b0100000000; + parameter [9:0] CHAN_BOND_SEQ_2_2 = 10'b0100000000; + parameter [9:0] CHAN_BOND_SEQ_2_3 = 10'b0100000000; + parameter [9:0] CHAN_BOND_SEQ_2_4 = 10'b0100000000; + parameter [3:0] CHAN_BOND_SEQ_2_ENABLE = 4'b1111; + parameter CHAN_BOND_SEQ_2_USE = "FALSE"; + parameter integer CHAN_BOND_SEQ_LEN = 1; + parameter CLK_CORRECT_USE = "TRUE"; + parameter CLK_COR_KEEP_IDLE = "FALSE"; + parameter integer CLK_COR_MAX_LAT = 20; + parameter integer CLK_COR_MIN_LAT = 18; + parameter CLK_COR_PRECEDENCE = "TRUE"; + parameter integer CLK_COR_REPEAT_WAIT = 0; + parameter [9:0] CLK_COR_SEQ_1_1 = 10'b0100011100; + parameter [9:0] CLK_COR_SEQ_1_2 = 10'b0000000000; + parameter [9:0] CLK_COR_SEQ_1_3 = 10'b0000000000; + parameter [9:0] CLK_COR_SEQ_1_4 = 10'b0000000000; + parameter [3:0] CLK_COR_SEQ_1_ENABLE = 4'b1111; + parameter [9:0] CLK_COR_SEQ_2_1 = 10'b0100000000; + parameter [9:0] CLK_COR_SEQ_2_2 = 10'b0100000000; + parameter [9:0] CLK_COR_SEQ_2_3 = 10'b0100000000; + parameter [9:0] CLK_COR_SEQ_2_4 = 10'b0100000000; + parameter [3:0] CLK_COR_SEQ_2_ENABLE = 4'b1111; + parameter CLK_COR_SEQ_2_USE = "FALSE"; + parameter integer CLK_COR_SEQ_LEN = 1; + parameter [28:0] CPLL_CFG = 29'h00BC07DC; + parameter integer CPLL_FBDIV = 4; + parameter integer CPLL_FBDIV_45 = 5; + parameter [23:0] CPLL_INIT_CFG = 24'h00001E; + parameter [15:0] CPLL_LOCK_CFG = 16'h01E8; + parameter integer CPLL_REFCLK_DIV = 1; + parameter DEC_MCOMMA_DETECT = "TRUE"; + parameter DEC_PCOMMA_DETECT = "TRUE"; + parameter DEC_VALID_COMMA_ONLY = "TRUE"; + parameter [23:0] DMONITOR_CFG = 24'h000A00; + parameter [0:0] ES_CLK_PHASE_SEL = 1'b0; + parameter [5:0] ES_CONTROL = 6'b000000; + parameter ES_ERRDET_EN = "FALSE"; + parameter ES_EYE_SCAN_EN = "TRUE"; + parameter [11:0] ES_HORZ_OFFSET = 12'h000; + parameter [9:0] ES_PMA_CFG = 10'b0000000000; + parameter [4:0] ES_PRESCALE = 5'b00000; + parameter [79:0] ES_QUALIFIER = 80'h00000000000000000000; + parameter [79:0] ES_QUAL_MASK = 80'h00000000000000000000; + parameter [79:0] ES_SDATA_MASK = 80'h00000000000000000000; + parameter [8:0] ES_VERT_OFFSET = 9'b000000000; + parameter [3:0] FTS_DESKEW_SEQ_ENABLE = 4'b1111; + parameter [3:0] FTS_LANE_DESKEW_CFG = 4'b1111; + parameter FTS_LANE_DESKEW_EN = "FALSE"; + parameter [2:0] GEARBOX_MODE = 3'b000; + parameter [0:0] IS_CLKRSVD0_INVERTED = 1'b0; + parameter [0:0] IS_CLKRSVD1_INVERTED = 1'b0; + parameter [0:0] IS_CPLLLOCKDETCLK_INVERTED = 1'b0; + parameter [0:0] IS_DMONITORCLK_INVERTED = 1'b0; + parameter [0:0] IS_DRPCLK_INVERTED = 1'b0; + parameter [0:0] IS_GTGREFCLK_INVERTED = 1'b0; + parameter [0:0] IS_RXUSRCLK2_INVERTED = 1'b0; + parameter [0:0] IS_RXUSRCLK_INVERTED = 1'b0; + parameter [0:0] IS_SIGVALIDCLK_INVERTED = 1'b0; + parameter [0:0] IS_TXPHDLYTSTCLK_INVERTED = 1'b0; + parameter [0:0] IS_TXUSRCLK2_INVERTED = 1'b0; + parameter [0:0] IS_TXUSRCLK_INVERTED = 1'b0; + parameter [0:0] LOOPBACK_CFG = 1'b0; + parameter [1:0] OUTREFCLK_SEL_INV = 2'b11; + parameter PCS_PCIE_EN = "FALSE"; + parameter [47:0] PCS_RSVD_ATTR = 48'h000000000000; + parameter [11:0] PD_TRANS_TIME_FROM_P2 = 12'h03C; + parameter [7:0] PD_TRANS_TIME_NONE_P2 = 8'h19; + parameter [7:0] PD_TRANS_TIME_TO_P2 = 8'h64; + parameter [31:0] PMA_RSV = 32'b00000000000000000000000010000000; + parameter [31:0] PMA_RSV2 = 32'b00011100000000000000000000001010; + parameter [1:0] PMA_RSV3 = 2'b00; + parameter [14:0] PMA_RSV4 = 15'b000000000001000; + parameter [3:0] PMA_RSV5 = 4'b0000; + parameter [0:0] RESET_POWERSAVE_DISABLE = 1'b0; + parameter [4:0] RXBUFRESET_TIME = 5'b00001; + parameter RXBUF_ADDR_MODE = "FULL"; + parameter [3:0] RXBUF_EIDLE_HI_CNT = 4'b1000; + parameter [3:0] RXBUF_EIDLE_LO_CNT = 4'b0000; + parameter RXBUF_EN = "TRUE"; + parameter RXBUF_RESET_ON_CB_CHANGE = "TRUE"; + parameter RXBUF_RESET_ON_COMMAALIGN = "FALSE"; + parameter RXBUF_RESET_ON_EIDLE = "FALSE"; + parameter RXBUF_RESET_ON_RATE_CHANGE = "TRUE"; + parameter integer RXBUF_THRESH_OVFLW = 61; + parameter RXBUF_THRESH_OVRD = "FALSE"; + parameter integer RXBUF_THRESH_UNDFLW = 4; + parameter [4:0] RXCDRFREQRESET_TIME = 5'b00001; + parameter [4:0] RXCDRPHRESET_TIME = 5'b00001; + parameter [82:0] RXCDR_CFG = 83'h0002007FE2000C208001A; + parameter [0:0] RXCDR_FR_RESET_ON_EIDLE = 1'b0; + parameter [0:0] RXCDR_HOLD_DURING_EIDLE = 1'b0; + parameter [5:0] RXCDR_LOCK_CFG = 6'b001001; + parameter [0:0] RXCDR_PH_RESET_ON_EIDLE = 1'b0; + parameter [6:0] RXDFELPMRESET_TIME = 7'b0001111; + parameter [15:0] RXDLY_CFG = 16'h001F; + parameter [8:0] RXDLY_LCFG = 9'h030; + parameter [15:0] RXDLY_TAP_CFG = 16'h0000; + parameter RXGEARBOX_EN = "FALSE"; + parameter [4:0] RXISCANRESET_TIME = 5'b00001; + parameter [13:0] RXLPM_HF_CFG = 14'b00001000000000; + parameter [17:0] RXLPM_LF_CFG = 18'b001001000000000000; + parameter [6:0] RXOOB_CFG = 7'b0000110; + parameter RXOOB_CLK_CFG = "PMA"; + parameter [4:0] RXOSCALRESET_TIME = 5'b00011; + parameter [4:0] RXOSCALRESET_TIMEOUT = 5'b00000; + parameter integer RXOUT_DIV = 2; + parameter [4:0] RXPCSRESET_TIME = 5'b00001; + parameter [23:0] RXPHDLY_CFG = 24'h084020; + parameter [23:0] RXPH_CFG = 24'hC00002; + parameter [4:0] RXPH_MONITOR_SEL = 5'b00000; + parameter [1:0] RXPI_CFG0 = 2'b00; + parameter [1:0] RXPI_CFG1 = 2'b00; + parameter [1:0] RXPI_CFG2 = 2'b00; + parameter [1:0] RXPI_CFG3 = 2'b00; + parameter [0:0] RXPI_CFG4 = 1'b0; + parameter [0:0] RXPI_CFG5 = 1'b0; + parameter [2:0] RXPI_CFG6 = 3'b100; + parameter [4:0] RXPMARESET_TIME = 5'b00011; + parameter [0:0] RXPRBS_ERR_LOOPBACK = 1'b0; + parameter integer RXSLIDE_AUTO_WAIT = 7; + parameter RXSLIDE_MODE = "OFF"; + parameter [0:0] RXSYNC_MULTILANE = 1'b0; + parameter [0:0] RXSYNC_OVRD = 1'b0; + parameter [0:0] RXSYNC_SKIP_DA = 1'b0; + parameter [23:0] RX_BIAS_CFG = 24'b000011000000000000010000; + parameter [5:0] RX_BUFFER_CFG = 6'b000000; + parameter integer RX_CLK25_DIV = 7; + parameter [0:0] RX_CLKMUX_PD = 1'b1; + parameter [1:0] RX_CM_SEL = 2'b11; + parameter [3:0] RX_CM_TRIM = 4'b0100; + parameter integer RX_DATA_WIDTH = 20; + parameter [5:0] RX_DDI_SEL = 6'b000000; + parameter [13:0] RX_DEBUG_CFG = 14'b00000000000000; + parameter RX_DEFER_RESET_BUF_EN = "TRUE"; + parameter [3:0] RX_DFELPM_CFG0 = 4'b0110; + parameter [0:0] RX_DFELPM_CFG1 = 1'b0; + parameter [0:0] RX_DFELPM_KLKH_AGC_STUP_EN = 1'b1; + parameter [1:0] RX_DFE_AGC_CFG0 = 2'b00; + parameter [2:0] RX_DFE_AGC_CFG1 = 3'b010; + parameter [3:0] RX_DFE_AGC_CFG2 = 4'b0000; + parameter [0:0] RX_DFE_AGC_OVRDEN = 1'b1; + parameter [22:0] RX_DFE_GAIN_CFG = 23'h0020C0; + parameter [11:0] RX_DFE_H2_CFG = 12'b000000000000; + parameter [11:0] RX_DFE_H3_CFG = 12'b000001000000; + parameter [10:0] RX_DFE_H4_CFG = 11'b00011100000; + parameter [10:0] RX_DFE_H5_CFG = 11'b00011100000; + parameter [10:0] RX_DFE_H6_CFG = 11'b00000100000; + parameter [10:0] RX_DFE_H7_CFG = 11'b00000100000; + parameter [32:0] RX_DFE_KL_CFG = 33'b000000000000000000000001100010000; + parameter [1:0] RX_DFE_KL_LPM_KH_CFG0 = 2'b01; + parameter [2:0] RX_DFE_KL_LPM_KH_CFG1 = 3'b010; + parameter [3:0] RX_DFE_KL_LPM_KH_CFG2 = 4'b0010; + parameter [0:0] RX_DFE_KL_LPM_KH_OVRDEN = 1'b1; + parameter [1:0] RX_DFE_KL_LPM_KL_CFG0 = 2'b10; + parameter [2:0] RX_DFE_KL_LPM_KL_CFG1 = 3'b010; + parameter [3:0] RX_DFE_KL_LPM_KL_CFG2 = 4'b0010; + parameter [0:0] RX_DFE_KL_LPM_KL_OVRDEN = 1'b1; + parameter [15:0] RX_DFE_LPM_CFG = 16'h0080; + parameter [0:0] RX_DFE_LPM_HOLD_DURING_EIDLE = 1'b0; + parameter [53:0] RX_DFE_ST_CFG = 54'h00E100000C003F; + parameter [16:0] RX_DFE_UT_CFG = 17'b00011100000000000; + parameter [16:0] RX_DFE_VP_CFG = 17'b00011101010100011; + parameter RX_DISPERR_SEQ_MATCH = "TRUE"; + parameter integer RX_INT_DATAWIDTH = 0; + parameter [12:0] RX_OS_CFG = 13'b0000010000000; + parameter integer RX_SIG_VALID_DLY = 10; + parameter RX_XCLK_SEL = "RXREC"; + parameter integer SAS_MAX_COM = 64; + parameter integer SAS_MIN_COM = 36; + parameter [3:0] SATA_BURST_SEQ_LEN = 4'b1111; + parameter [2:0] SATA_BURST_VAL = 3'b100; + parameter SATA_CPLL_CFG = "VCO_3000MHZ"; + parameter [2:0] SATA_EIDLE_VAL = 3'b100; + parameter integer SATA_MAX_BURST = 8; + parameter integer SATA_MAX_INIT = 21; + parameter integer SATA_MAX_WAKE = 7; + parameter integer SATA_MIN_BURST = 4; + parameter integer SATA_MIN_INIT = 12; + parameter integer SATA_MIN_WAKE = 4; + parameter SHOW_REALIGN_COMMA = "TRUE"; + parameter [2:0] SIM_CPLLREFCLK_SEL = 3'b001; + parameter SIM_RECEIVER_DETECT_PASS = "TRUE"; + parameter SIM_RESET_SPEEDUP = "TRUE"; + parameter SIM_TX_EIDLE_DRIVE_LEVEL = "X"; + parameter SIM_VERSION = "1.1"; + parameter [14:0] TERM_RCAL_CFG = 15'b100001000010000; + parameter [2:0] TERM_RCAL_OVRD = 3'b000; + parameter [7:0] TRANS_TIME_RATE = 8'h0E; + parameter [31:0] TST_RSV = 32'h00000000; + parameter TXBUF_EN = "TRUE"; + parameter TXBUF_RESET_ON_RATE_CHANGE = "FALSE"; + parameter [15:0] TXDLY_CFG = 16'h001F; + parameter [8:0] TXDLY_LCFG = 9'h030; + parameter [15:0] TXDLY_TAP_CFG = 16'h0000; + parameter TXGEARBOX_EN = "FALSE"; + parameter [0:0] TXOOB_CFG = 1'b0; + parameter integer TXOUT_DIV = 2; + parameter [4:0] TXPCSRESET_TIME = 5'b00001; + parameter [23:0] TXPHDLY_CFG = 24'h084020; + parameter [15:0] TXPH_CFG = 16'h0780; + parameter [4:0] TXPH_MONITOR_SEL = 5'b00000; + parameter [1:0] TXPI_CFG0 = 2'b00; + parameter [1:0] TXPI_CFG1 = 2'b00; + parameter [1:0] TXPI_CFG2 = 2'b00; + parameter [0:0] TXPI_CFG3 = 1'b0; + parameter [0:0] TXPI_CFG4 = 1'b0; + parameter [2:0] TXPI_CFG5 = 3'b100; + parameter [0:0] TXPI_GREY_SEL = 1'b0; + parameter [0:0] TXPI_INVSTROBE_SEL = 1'b0; + parameter TXPI_PPMCLK_SEL = "TXUSRCLK2"; + parameter [7:0] TXPI_PPM_CFG = 8'b00000000; + parameter [2:0] TXPI_SYNFREQ_PPM = 3'b000; + parameter [4:0] TXPMARESET_TIME = 5'b00001; + parameter [0:0] TXSYNC_MULTILANE = 1'b0; + parameter [0:0] TXSYNC_OVRD = 1'b0; + parameter [0:0] TXSYNC_SKIP_DA = 1'b0; + parameter integer TX_CLK25_DIV = 7; + parameter [0:0] TX_CLKMUX_PD = 1'b1; + parameter integer TX_DATA_WIDTH = 20; + parameter [5:0] TX_DEEMPH0 = 6'b000000; + parameter [5:0] TX_DEEMPH1 = 6'b000000; + parameter TX_DRIVE_MODE = "DIRECT"; + parameter [2:0] TX_EIDLE_ASSERT_DELAY = 3'b110; + parameter [2:0] TX_EIDLE_DEASSERT_DELAY = 3'b100; + parameter integer TX_INT_DATAWIDTH = 0; + parameter TX_LOOPBACK_DRIVE_HIZ = "FALSE"; + parameter [0:0] TX_MAINCURSOR_SEL = 1'b0; + parameter [6:0] TX_MARGIN_FULL_0 = 7'b1001110; + parameter [6:0] TX_MARGIN_FULL_1 = 7'b1001001; + parameter [6:0] TX_MARGIN_FULL_2 = 7'b1000101; + parameter [6:0] TX_MARGIN_FULL_3 = 7'b1000010; + parameter [6:0] TX_MARGIN_FULL_4 = 7'b1000000; + parameter [6:0] TX_MARGIN_LOW_0 = 7'b1000110; + parameter [6:0] TX_MARGIN_LOW_1 = 7'b1000100; + parameter [6:0] TX_MARGIN_LOW_2 = 7'b1000010; + parameter [6:0] TX_MARGIN_LOW_3 = 7'b1000000; + parameter [6:0] TX_MARGIN_LOW_4 = 7'b1000000; + parameter [0:0] TX_QPI_STATUS_EN = 1'b0; + parameter [13:0] TX_RXDETECT_CFG = 14'h1832; + parameter [16:0] TX_RXDETECT_PRECHARGE_TIME = 17'h00000; + parameter [2:0] TX_RXDETECT_REF = 3'b100; + parameter TX_XCLK_SEL = "TXUSR"; + parameter [0:0] UCODEER_CLR = 1'b0; + parameter [0:0] USE_PCS_CLK_PHASE_SEL = 1'b0; + output CPLLFBCLKLOST; + output CPLLLOCK; + output CPLLREFCLKLOST; + output DRPRDY; + output EYESCANDATAERROR; + output GTHTXN; + output GTHTXP; + output GTREFCLKMONITOR; + output PHYSTATUS; + output RSOSINTDONE; + output RXBYTEISALIGNED; + output RXBYTEREALIGN; + output RXCDRLOCK; + output RXCHANBONDSEQ; + output RXCHANISALIGNED; + output RXCHANREALIGN; + output RXCOMINITDET; + output RXCOMMADET; + output RXCOMSASDET; + output RXCOMWAKEDET; + output RXDFESLIDETAPSTARTED; + output RXDFESLIDETAPSTROBEDONE; + output RXDFESLIDETAPSTROBESTARTED; + output RXDFESTADAPTDONE; + output RXDLYSRESETDONE; + output RXELECIDLE; + output RXOSINTSTARTED; + output RXOSINTSTROBEDONE; + output RXOSINTSTROBESTARTED; + output RXOUTCLK; + output RXOUTCLKFABRIC; + output RXOUTCLKPCS; + output RXPHALIGNDONE; + output RXPMARESETDONE; + output RXPRBSERR; + output RXQPISENN; + output RXQPISENP; + output RXRATEDONE; + output RXRESETDONE; + output RXSYNCDONE; + output RXSYNCOUT; + output RXVALID; + output TXCOMFINISH; + output TXDLYSRESETDONE; + output TXGEARBOXREADY; + output TXOUTCLK; + output TXOUTCLKFABRIC; + output TXOUTCLKPCS; + output TXPHALIGNDONE; + output TXPHINITDONE; + output TXPMARESETDONE; + output TXQPISENN; + output TXQPISENP; + output TXRATEDONE; + output TXRESETDONE; + output TXSYNCDONE; + output TXSYNCOUT; + output [14:0] DMONITOROUT; + output [15:0] DRPDO; + output [15:0] PCSRSVDOUT; + output [1:0] RXCLKCORCNT; + output [1:0] RXDATAVALID; + output [1:0] RXHEADERVALID; + output [1:0] RXSTARTOFSEQ; + output [1:0] TXBUFSTATUS; + output [2:0] RXBUFSTATUS; + output [2:0] RXSTATUS; + output [4:0] RXCHBONDO; + output [4:0] RXPHMONITOR; + output [4:0] RXPHSLIPMONITOR; + output [5:0] RXHEADER; + output [63:0] RXDATA; + output [6:0] RXMONITOROUT; + output [7:0] RXCHARISCOMMA; + output [7:0] RXCHARISK; + output [7:0] RXDISPERR; + output [7:0] RXNOTINTABLE; + input CFGRESET; + input CLKRSVD0; + input CLKRSVD1; + input CPLLLOCKDETCLK; + input CPLLLOCKEN; + input CPLLPD; + input CPLLRESET; + input DMONFIFORESET; + input DMONITORCLK; + input DRPCLK; + input DRPEN; + input DRPWE; + input EYESCANMODE; + input EYESCANRESET; + input EYESCANTRIGGER; + input GTGREFCLK; + input GTHRXN; + input GTHRXP; + input GTNORTHREFCLK0; + input GTNORTHREFCLK1; + input GTREFCLK0; + input GTREFCLK1; + input GTRESETSEL; + input GTRXRESET; + input GTSOUTHREFCLK0; + input GTSOUTHREFCLK1; + input GTTXRESET; + input QPLLCLK; + input QPLLREFCLK; + input RESETOVRD; + input RX8B10BEN; + input RXBUFRESET; + input RXCDRFREQRESET; + input RXCDRHOLD; + input RXCDROVRDEN; + input RXCDRRESET; + input RXCDRRESETRSV; + input RXCHBONDEN; + input RXCHBONDMASTER; + input RXCHBONDSLAVE; + input RXCOMMADETEN; + input RXDDIEN; + input RXDFEAGCHOLD; + input RXDFEAGCOVRDEN; + input RXDFECM1EN; + input RXDFELFHOLD; + input RXDFELFOVRDEN; + input RXDFELPMRESET; + input RXDFESLIDETAPADAPTEN; + input RXDFESLIDETAPHOLD; + input RXDFESLIDETAPINITOVRDEN; + input RXDFESLIDETAPONLYADAPTEN; + input RXDFESLIDETAPOVRDEN; + input RXDFESLIDETAPSTROBE; + input RXDFETAP2HOLD; + input RXDFETAP2OVRDEN; + input RXDFETAP3HOLD; + input RXDFETAP3OVRDEN; + input RXDFETAP4HOLD; + input RXDFETAP4OVRDEN; + input RXDFETAP5HOLD; + input RXDFETAP5OVRDEN; + input RXDFETAP6HOLD; + input RXDFETAP6OVRDEN; + input RXDFETAP7HOLD; + input RXDFETAP7OVRDEN; + input RXDFEUTHOLD; + input RXDFEUTOVRDEN; + input RXDFEVPHOLD; + input RXDFEVPOVRDEN; + input RXDFEVSEN; + input RXDFEXYDEN; + input RXDLYBYPASS; + input RXDLYEN; + input RXDLYOVRDEN; + input RXDLYSRESET; + input RXGEARBOXSLIP; + input RXLPMEN; + input RXLPMHFHOLD; + input RXLPMHFOVRDEN; + input RXLPMLFHOLD; + input RXLPMLFKLOVRDEN; + input RXMCOMMAALIGNEN; + input RXOOBRESET; + input RXOSCALRESET; + input RXOSHOLD; + input RXOSINTEN; + input RXOSINTHOLD; + input RXOSINTNTRLEN; + input RXOSINTOVRDEN; + input RXOSINTSTROBE; + input RXOSINTTESTOVRDEN; + input RXOSOVRDEN; + input RXPCOMMAALIGNEN; + input RXPCSRESET; + input RXPHALIGN; + input RXPHALIGNEN; + input RXPHDLYPD; + input RXPHDLYRESET; + input RXPHOVRDEN; + input RXPMARESET; + input RXPOLARITY; + input RXPRBSCNTRESET; + input RXQPIEN; + input RXRATEMODE; + input RXSLIDE; + input RXSYNCALLIN; + input RXSYNCIN; + input RXSYNCMODE; + input RXUSERRDY; + input RXUSRCLK2; + input RXUSRCLK; + input SETERRSTATUS; + input SIGVALIDCLK; + input TX8B10BEN; + input TXCOMINIT; + input TXCOMSAS; + input TXCOMWAKE; + input TXDEEMPH; + input TXDETECTRX; + input TXDIFFPD; + input TXDLYBYPASS; + input TXDLYEN; + input TXDLYHOLD; + input TXDLYOVRDEN; + input TXDLYSRESET; + input TXDLYUPDOWN; + input TXELECIDLE; + input TXINHIBIT; + input TXPCSRESET; + input TXPDELECIDLEMODE; + input TXPHALIGN; + input TXPHALIGNEN; + input TXPHDLYPD; + input TXPHDLYRESET; + input TXPHDLYTSTCLK; + input TXPHINIT; + input TXPHOVRDEN; + input TXPIPPMEN; + input TXPIPPMOVRDEN; + input TXPIPPMPD; + input TXPIPPMSEL; + input TXPISOPD; + input TXPMARESET; + input TXPOLARITY; + input TXPOSTCURSORINV; + input TXPRBSFORCEERR; + input TXPRECURSORINV; + input TXQPIBIASEN; + input TXQPISTRONGPDOWN; + input TXQPIWEAKPUP; + input TXRATEMODE; + input TXSTARTSEQ; + input TXSWING; + input TXSYNCALLIN; + input TXSYNCIN; + input TXSYNCMODE; + input TXUSERRDY; + input TXUSRCLK2; + input TXUSRCLK; + input [13:0] RXADAPTSELTEST; + input [15:0] DRPDI; + input [15:0] GTRSVD; + input [15:0] PCSRSVDIN; + input [19:0] TSTIN; + input [1:0] RXELECIDLEMODE; + input [1:0] RXMONITORSEL; + input [1:0] RXPD; + input [1:0] RXSYSCLKSEL; + input [1:0] TXPD; + input [1:0] TXSYSCLKSEL; + input [2:0] CPLLREFCLKSEL; + input [2:0] LOOPBACK; + input [2:0] RXCHBONDLEVEL; + input [2:0] RXOUTCLKSEL; + input [2:0] RXPRBSSEL; + input [2:0] RXRATE; + input [2:0] TXBUFDIFFCTRL; + input [2:0] TXHEADER; + input [2:0] TXMARGIN; + input [2:0] TXOUTCLKSEL; + input [2:0] TXPRBSSEL; + input [2:0] TXRATE; + input [3:0] RXOSINTCFG; + input [3:0] RXOSINTID0; + input [3:0] TXDIFFCTRL; + input [4:0] PCSRSVDIN2; + input [4:0] PMARSVDIN; + input [4:0] RXCHBONDI; + input [4:0] RXDFEAGCTRL; + input [4:0] RXDFESLIDETAP; + input [4:0] TXPIPPMSTEPSIZE; + input [4:0] TXPOSTCURSOR; + input [4:0] TXPRECURSOR; + input [5:0] RXDFESLIDETAPID; + input [63:0] TXDATA; + input [6:0] TXMAINCURSOR; + input [6:0] TXSEQUENCE; + input [7:0] TX8B10BBYPASS; + input [7:0] TXCHARDISPMODE; + input [7:0] TXCHARDISPVAL; + input [7:0] TXCHARISK; + input [8:0] DRPADDR; +endmodule + +module GTHE2_COMMON (...); + parameter [63:0] BIAS_CFG = 64'h0000040000001000; + parameter [31:0] COMMON_CFG = 32'h0000001C; + parameter [0:0] IS_DRPCLK_INVERTED = 1'b0; + parameter [0:0] IS_GTGREFCLK_INVERTED = 1'b0; + parameter [0:0] IS_QPLLLOCKDETCLK_INVERTED = 1'b0; + parameter [26:0] QPLL_CFG = 27'h0480181; + parameter [3:0] QPLL_CLKOUT_CFG = 4'b0000; + parameter [5:0] QPLL_COARSE_FREQ_OVRD = 6'b010000; + parameter [0:0] QPLL_COARSE_FREQ_OVRD_EN = 1'b0; + parameter [9:0] QPLL_CP = 10'b0000011111; + parameter [0:0] QPLL_CP_MONITOR_EN = 1'b0; + parameter [0:0] QPLL_DMONITOR_SEL = 1'b0; + parameter [9:0] QPLL_FBDIV = 10'b0000000000; + parameter [0:0] QPLL_FBDIV_MONITOR_EN = 1'b0; + parameter [0:0] QPLL_FBDIV_RATIO = 1'b0; + parameter [23:0] QPLL_INIT_CFG = 24'h000006; + parameter [15:0] QPLL_LOCK_CFG = 16'h01E8; + parameter [3:0] QPLL_LPF = 4'b1111; + parameter integer QPLL_REFCLK_DIV = 2; + parameter [0:0] QPLL_RP_COMP = 1'b0; + parameter [1:0] QPLL_VTRL_RESET = 2'b00; + parameter [1:0] RCAL_CFG = 2'b00; + parameter [15:0] RSVD_ATTR0 = 16'h0000; + parameter [15:0] RSVD_ATTR1 = 16'h0000; + parameter [2:0] SIM_QPLLREFCLK_SEL = 3'b001; + parameter SIM_RESET_SPEEDUP = "TRUE"; + parameter SIM_VERSION = "1.1"; + output DRPRDY; + output QPLLFBCLKLOST; + output QPLLLOCK; + output QPLLOUTCLK; + output QPLLOUTREFCLK; + output QPLLREFCLKLOST; + output REFCLKOUTMONITOR; + output [15:0] DRPDO; + output [15:0] PMARSVDOUT; + output [7:0] QPLLDMONITOR; + input BGBYPASSB; + input BGMONITORENB; + input BGPDB; + input BGRCALOVRDENB; + input DRPCLK; + input DRPEN; + input DRPWE; + input GTGREFCLK; + input GTNORTHREFCLK0; + input GTNORTHREFCLK1; + input GTREFCLK0; + input GTREFCLK1; + input GTSOUTHREFCLK0; + input GTSOUTHREFCLK1; + input QPLLLOCKDETCLK; + input QPLLLOCKEN; + input QPLLOUTRESET; + input QPLLPD; + input QPLLRESET; + input RCALENB; + input [15:0] DRPDI; + input [15:0] QPLLRSVD1; + input [2:0] QPLLREFCLKSEL; + input [4:0] BGRCALOVRD; + input [4:0] QPLLRSVD2; + input [7:0] DRPADDR; + input [7:0] PMARSVD; +endmodule + +module GTPE2_CHANNEL (...); + parameter [0:0] ACJTAG_DEBUG_MODE = 1'b0; + parameter [0:0] ACJTAG_MODE = 1'b0; + parameter [0:0] ACJTAG_RESET = 1'b0; + parameter [19:0] ADAPT_CFG0 = 20'b00000000000000000000; + parameter ALIGN_COMMA_DOUBLE = "FALSE"; + parameter [9:0] ALIGN_COMMA_ENABLE = 10'b0001111111; + parameter integer ALIGN_COMMA_WORD = 1; + parameter ALIGN_MCOMMA_DET = "TRUE"; + parameter [9:0] ALIGN_MCOMMA_VALUE = 10'b1010000011; + parameter ALIGN_PCOMMA_DET = "TRUE"; + parameter [9:0] ALIGN_PCOMMA_VALUE = 10'b0101111100; + parameter CBCC_DATA_SOURCE_SEL = "DECODED"; + parameter [42:0] CFOK_CFG = 43'b1001001000000000000000001000000111010000000; + parameter [6:0] CFOK_CFG2 = 7'b0100000; + parameter [6:0] CFOK_CFG3 = 7'b0100000; + parameter [0:0] CFOK_CFG4 = 1'b0; + parameter [1:0] CFOK_CFG5 = 2'b00; + parameter [3:0] CFOK_CFG6 = 4'b0000; + parameter CHAN_BOND_KEEP_ALIGN = "FALSE"; + parameter integer CHAN_BOND_MAX_SKEW = 7; + parameter [9:0] CHAN_BOND_SEQ_1_1 = 10'b0101111100; + parameter [9:0] CHAN_BOND_SEQ_1_2 = 10'b0000000000; + parameter [9:0] CHAN_BOND_SEQ_1_3 = 10'b0000000000; + parameter [9:0] CHAN_BOND_SEQ_1_4 = 10'b0000000000; + parameter [3:0] CHAN_BOND_SEQ_1_ENABLE = 4'b1111; + parameter [9:0] CHAN_BOND_SEQ_2_1 = 10'b0100000000; + parameter [9:0] CHAN_BOND_SEQ_2_2 = 10'b0100000000; + parameter [9:0] CHAN_BOND_SEQ_2_3 = 10'b0100000000; + parameter [9:0] CHAN_BOND_SEQ_2_4 = 10'b0100000000; + parameter [3:0] CHAN_BOND_SEQ_2_ENABLE = 4'b1111; + parameter CHAN_BOND_SEQ_2_USE = "FALSE"; + parameter integer CHAN_BOND_SEQ_LEN = 1; + parameter [0:0] CLK_COMMON_SWING = 1'b0; + parameter CLK_CORRECT_USE = "TRUE"; + parameter CLK_COR_KEEP_IDLE = "FALSE"; + parameter integer CLK_COR_MAX_LAT = 20; + parameter integer CLK_COR_MIN_LAT = 18; + parameter CLK_COR_PRECEDENCE = "TRUE"; + parameter integer CLK_COR_REPEAT_WAIT = 0; + parameter [9:0] CLK_COR_SEQ_1_1 = 10'b0100011100; + parameter [9:0] CLK_COR_SEQ_1_2 = 10'b0000000000; + parameter [9:0] CLK_COR_SEQ_1_3 = 10'b0000000000; + parameter [9:0] CLK_COR_SEQ_1_4 = 10'b0000000000; + parameter [3:0] CLK_COR_SEQ_1_ENABLE = 4'b1111; + parameter [9:0] CLK_COR_SEQ_2_1 = 10'b0100000000; + parameter [9:0] CLK_COR_SEQ_2_2 = 10'b0100000000; + parameter [9:0] CLK_COR_SEQ_2_3 = 10'b0100000000; + parameter [9:0] CLK_COR_SEQ_2_4 = 10'b0100000000; + parameter [3:0] CLK_COR_SEQ_2_ENABLE = 4'b1111; + parameter CLK_COR_SEQ_2_USE = "FALSE"; + parameter integer CLK_COR_SEQ_LEN = 1; + parameter DEC_MCOMMA_DETECT = "TRUE"; + parameter DEC_PCOMMA_DETECT = "TRUE"; + parameter DEC_VALID_COMMA_ONLY = "TRUE"; + parameter [23:0] DMONITOR_CFG = 24'h000A00; + parameter [0:0] ES_CLK_PHASE_SEL = 1'b0; + parameter [5:0] ES_CONTROL = 6'b000000; + parameter ES_ERRDET_EN = "FALSE"; + parameter ES_EYE_SCAN_EN = "FALSE"; + parameter [11:0] ES_HORZ_OFFSET = 12'h010; + parameter [9:0] ES_PMA_CFG = 10'b0000000000; + parameter [4:0] ES_PRESCALE = 5'b00000; + parameter [79:0] ES_QUALIFIER = 80'h00000000000000000000; + parameter [79:0] ES_QUAL_MASK = 80'h00000000000000000000; + parameter [79:0] ES_SDATA_MASK = 80'h00000000000000000000; + parameter [8:0] ES_VERT_OFFSET = 9'b000000000; + parameter [3:0] FTS_DESKEW_SEQ_ENABLE = 4'b1111; + parameter [3:0] FTS_LANE_DESKEW_CFG = 4'b1111; + parameter FTS_LANE_DESKEW_EN = "FALSE"; + parameter [2:0] GEARBOX_MODE = 3'b000; + parameter [0:0] IS_CLKRSVD0_INVERTED = 1'b0; + parameter [0:0] IS_CLKRSVD1_INVERTED = 1'b0; + parameter [0:0] IS_DMONITORCLK_INVERTED = 1'b0; + parameter [0:0] IS_DRPCLK_INVERTED = 1'b0; + parameter [0:0] IS_RXUSRCLK2_INVERTED = 1'b0; + parameter [0:0] IS_RXUSRCLK_INVERTED = 1'b0; + parameter [0:0] IS_SIGVALIDCLK_INVERTED = 1'b0; + parameter [0:0] IS_TXPHDLYTSTCLK_INVERTED = 1'b0; + parameter [0:0] IS_TXUSRCLK2_INVERTED = 1'b0; + parameter [0:0] IS_TXUSRCLK_INVERTED = 1'b0; + parameter [0:0] LOOPBACK_CFG = 1'b0; + parameter [1:0] OUTREFCLK_SEL_INV = 2'b11; + parameter PCS_PCIE_EN = "FALSE"; + parameter [47:0] PCS_RSVD_ATTR = 48'h000000000000; + parameter [11:0] PD_TRANS_TIME_FROM_P2 = 12'h03C; + parameter [7:0] PD_TRANS_TIME_NONE_P2 = 8'h19; + parameter [7:0] PD_TRANS_TIME_TO_P2 = 8'h64; + parameter [0:0] PMA_LOOPBACK_CFG = 1'b0; + parameter [31:0] PMA_RSV = 32'h00000333; + parameter [31:0] PMA_RSV2 = 32'h00002050; + parameter [1:0] PMA_RSV3 = 2'b00; + parameter [3:0] PMA_RSV4 = 4'b0000; + parameter [0:0] PMA_RSV5 = 1'b0; + parameter [0:0] PMA_RSV6 = 1'b0; + parameter [0:0] PMA_RSV7 = 1'b0; + parameter [4:0] RXBUFRESET_TIME = 5'b00001; + parameter RXBUF_ADDR_MODE = "FULL"; + parameter [3:0] RXBUF_EIDLE_HI_CNT = 4'b1000; + parameter [3:0] RXBUF_EIDLE_LO_CNT = 4'b0000; + parameter RXBUF_EN = "TRUE"; + parameter RXBUF_RESET_ON_CB_CHANGE = "TRUE"; + parameter RXBUF_RESET_ON_COMMAALIGN = "FALSE"; + parameter RXBUF_RESET_ON_EIDLE = "FALSE"; + parameter RXBUF_RESET_ON_RATE_CHANGE = "TRUE"; + parameter integer RXBUF_THRESH_OVFLW = 61; + parameter RXBUF_THRESH_OVRD = "FALSE"; + parameter integer RXBUF_THRESH_UNDFLW = 4; + parameter [4:0] RXCDRFREQRESET_TIME = 5'b00001; + parameter [4:0] RXCDRPHRESET_TIME = 5'b00001; + parameter [82:0] RXCDR_CFG = 83'h0000107FE406001041010; + parameter [0:0] RXCDR_FR_RESET_ON_EIDLE = 1'b0; + parameter [0:0] RXCDR_HOLD_DURING_EIDLE = 1'b0; + parameter [5:0] RXCDR_LOCK_CFG = 6'b001001; + parameter [0:0] RXCDR_PH_RESET_ON_EIDLE = 1'b0; + parameter [15:0] RXDLY_CFG = 16'h0010; + parameter [8:0] RXDLY_LCFG = 9'h020; + parameter [15:0] RXDLY_TAP_CFG = 16'h0000; + parameter RXGEARBOX_EN = "FALSE"; + parameter [4:0] RXISCANRESET_TIME = 5'b00001; + parameter [6:0] RXLPMRESET_TIME = 7'b0001111; + parameter [0:0] RXLPM_BIAS_STARTUP_DISABLE = 1'b0; + parameter [3:0] RXLPM_CFG = 4'b0110; + parameter [0:0] RXLPM_CFG1 = 1'b0; + parameter [0:0] RXLPM_CM_CFG = 1'b0; + parameter [8:0] RXLPM_GC_CFG = 9'b111100010; + parameter [2:0] RXLPM_GC_CFG2 = 3'b001; + parameter [13:0] RXLPM_HF_CFG = 14'b00001111110000; + parameter [4:0] RXLPM_HF_CFG2 = 5'b01010; + parameter [3:0] RXLPM_HF_CFG3 = 4'b0000; + parameter [0:0] RXLPM_HOLD_DURING_EIDLE = 1'b0; + parameter [0:0] RXLPM_INCM_CFG = 1'b0; + parameter [0:0] RXLPM_IPCM_CFG = 1'b0; + parameter [17:0] RXLPM_LF_CFG = 18'b000000001111110000; + parameter [4:0] RXLPM_LF_CFG2 = 5'b01010; + parameter [2:0] RXLPM_OSINT_CFG = 3'b100; + parameter [6:0] RXOOB_CFG = 7'b0000110; + parameter RXOOB_CLK_CFG = "PMA"; + parameter [4:0] RXOSCALRESET_TIME = 5'b00011; + parameter [4:0] RXOSCALRESET_TIMEOUT = 5'b00000; + parameter integer RXOUT_DIV = 2; + parameter [4:0] RXPCSRESET_TIME = 5'b00001; + parameter [23:0] RXPHDLY_CFG = 24'h084000; + parameter [23:0] RXPH_CFG = 24'hC00002; + parameter [4:0] RXPH_MONITOR_SEL = 5'b00000; + parameter [2:0] RXPI_CFG0 = 3'b000; + parameter [0:0] RXPI_CFG1 = 1'b0; + parameter [0:0] RXPI_CFG2 = 1'b0; + parameter [4:0] RXPMARESET_TIME = 5'b00011; + parameter [0:0] RXPRBS_ERR_LOOPBACK = 1'b0; + parameter integer RXSLIDE_AUTO_WAIT = 7; + parameter RXSLIDE_MODE = "OFF"; + parameter [0:0] RXSYNC_MULTILANE = 1'b0; + parameter [0:0] RXSYNC_OVRD = 1'b0; + parameter [0:0] RXSYNC_SKIP_DA = 1'b0; + parameter [15:0] RX_BIAS_CFG = 16'b0000111100110011; + parameter [5:0] RX_BUFFER_CFG = 6'b000000; + parameter integer RX_CLK25_DIV = 7; + parameter [0:0] RX_CLKMUX_EN = 1'b1; + parameter [1:0] RX_CM_SEL = 2'b11; + parameter [3:0] RX_CM_TRIM = 4'b0100; + parameter integer RX_DATA_WIDTH = 20; + parameter [5:0] RX_DDI_SEL = 6'b000000; + parameter [13:0] RX_DEBUG_CFG = 14'b00000000000000; + parameter RX_DEFER_RESET_BUF_EN = "TRUE"; + parameter RX_DISPERR_SEQ_MATCH = "TRUE"; + parameter [12:0] RX_OS_CFG = 13'b0001111110000; + parameter integer RX_SIG_VALID_DLY = 10; + parameter RX_XCLK_SEL = "RXREC"; + parameter integer SAS_MAX_COM = 64; + parameter integer SAS_MIN_COM = 36; + parameter [3:0] SATA_BURST_SEQ_LEN = 4'b1111; + parameter [2:0] SATA_BURST_VAL = 3'b100; + parameter [2:0] SATA_EIDLE_VAL = 3'b100; + parameter integer SATA_MAX_BURST = 8; + parameter integer SATA_MAX_INIT = 21; + parameter integer SATA_MAX_WAKE = 7; + parameter integer SATA_MIN_BURST = 4; + parameter integer SATA_MIN_INIT = 12; + parameter integer SATA_MIN_WAKE = 4; + parameter SATA_PLL_CFG = "VCO_3000MHZ"; + parameter SHOW_REALIGN_COMMA = "TRUE"; + parameter SIM_RECEIVER_DETECT_PASS = "TRUE"; + parameter SIM_RESET_SPEEDUP = "TRUE"; + parameter SIM_TX_EIDLE_DRIVE_LEVEL = "X"; + parameter SIM_VERSION = "1.0"; + parameter [14:0] TERM_RCAL_CFG = 15'b100001000010000; + parameter [2:0] TERM_RCAL_OVRD = 3'b000; + parameter [7:0] TRANS_TIME_RATE = 8'h0E; + parameter [31:0] TST_RSV = 32'h00000000; + parameter TXBUF_EN = "TRUE"; + parameter TXBUF_RESET_ON_RATE_CHANGE = "FALSE"; + parameter [15:0] TXDLY_CFG = 16'h0010; + parameter [8:0] TXDLY_LCFG = 9'h020; + parameter [15:0] TXDLY_TAP_CFG = 16'h0000; + parameter TXGEARBOX_EN = "FALSE"; + parameter [0:0] TXOOB_CFG = 1'b0; + parameter integer TXOUT_DIV = 2; + parameter [4:0] TXPCSRESET_TIME = 5'b00001; + parameter [23:0] TXPHDLY_CFG = 24'h084000; + parameter [15:0] TXPH_CFG = 16'h0400; + parameter [4:0] TXPH_MONITOR_SEL = 5'b00000; + parameter [1:0] TXPI_CFG0 = 2'b00; + parameter [1:0] TXPI_CFG1 = 2'b00; + parameter [1:0] TXPI_CFG2 = 2'b00; + parameter [0:0] TXPI_CFG3 = 1'b0; + parameter [0:0] TXPI_CFG4 = 1'b0; + parameter [2:0] TXPI_CFG5 = 3'b000; + parameter [0:0] TXPI_GREY_SEL = 1'b0; + parameter [0:0] TXPI_INVSTROBE_SEL = 1'b0; + parameter TXPI_PPMCLK_SEL = "TXUSRCLK2"; + parameter [7:0] TXPI_PPM_CFG = 8'b00000000; + parameter [2:0] TXPI_SYNFREQ_PPM = 3'b000; + parameter [4:0] TXPMARESET_TIME = 5'b00001; + parameter [0:0] TXSYNC_MULTILANE = 1'b0; + parameter [0:0] TXSYNC_OVRD = 1'b0; + parameter [0:0] TXSYNC_SKIP_DA = 1'b0; + parameter integer TX_CLK25_DIV = 7; + parameter [0:0] TX_CLKMUX_EN = 1'b1; + parameter integer TX_DATA_WIDTH = 20; + parameter [5:0] TX_DEEMPH0 = 6'b000000; + parameter [5:0] TX_DEEMPH1 = 6'b000000; + parameter TX_DRIVE_MODE = "DIRECT"; + parameter [2:0] TX_EIDLE_ASSERT_DELAY = 3'b110; + parameter [2:0] TX_EIDLE_DEASSERT_DELAY = 3'b100; + parameter TX_LOOPBACK_DRIVE_HIZ = "FALSE"; + parameter [0:0] TX_MAINCURSOR_SEL = 1'b0; + parameter [6:0] TX_MARGIN_FULL_0 = 7'b1001110; + parameter [6:0] TX_MARGIN_FULL_1 = 7'b1001001; + parameter [6:0] TX_MARGIN_FULL_2 = 7'b1000101; + parameter [6:0] TX_MARGIN_FULL_3 = 7'b1000010; + parameter [6:0] TX_MARGIN_FULL_4 = 7'b1000000; + parameter [6:0] TX_MARGIN_LOW_0 = 7'b1000110; + parameter [6:0] TX_MARGIN_LOW_1 = 7'b1000100; + parameter [6:0] TX_MARGIN_LOW_2 = 7'b1000010; + parameter [6:0] TX_MARGIN_LOW_3 = 7'b1000000; + parameter [6:0] TX_MARGIN_LOW_4 = 7'b1000000; + parameter [0:0] TX_PREDRIVER_MODE = 1'b0; + parameter [13:0] TX_RXDETECT_CFG = 14'h1832; + parameter [2:0] TX_RXDETECT_REF = 3'b100; + parameter TX_XCLK_SEL = "TXUSR"; + parameter [0:0] UCODEER_CLR = 1'b0; + parameter [0:0] USE_PCS_CLK_PHASE_SEL = 1'b0; + output DRPRDY; + output EYESCANDATAERROR; + output GTPTXN; + output GTPTXP; + output PHYSTATUS; + output PMARSVDOUT0; + output PMARSVDOUT1; + output RXBYTEISALIGNED; + output RXBYTEREALIGN; + output RXCDRLOCK; + output RXCHANBONDSEQ; + output RXCHANISALIGNED; + output RXCHANREALIGN; + output RXCOMINITDET; + output RXCOMMADET; + output RXCOMSASDET; + output RXCOMWAKEDET; + output RXDLYSRESETDONE; + output RXELECIDLE; + output RXHEADERVALID; + output RXOSINTDONE; + output RXOSINTSTARTED; + output RXOSINTSTROBEDONE; + output RXOSINTSTROBESTARTED; + output RXOUTCLK; + output RXOUTCLKFABRIC; + output RXOUTCLKPCS; + output RXPHALIGNDONE; + output RXPMARESETDONE; + output RXPRBSERR; + output RXRATEDONE; + output RXRESETDONE; + output RXSYNCDONE; + output RXSYNCOUT; + output RXVALID; + output TXCOMFINISH; + output TXDLYSRESETDONE; + output TXGEARBOXREADY; + output TXOUTCLK; + output TXOUTCLKFABRIC; + output TXOUTCLKPCS; + output TXPHALIGNDONE; + output TXPHINITDONE; + output TXPMARESETDONE; + output TXRATEDONE; + output TXRESETDONE; + output TXSYNCDONE; + output TXSYNCOUT; + output [14:0] DMONITOROUT; + output [15:0] DRPDO; + output [15:0] PCSRSVDOUT; + output [1:0] RXCLKCORCNT; + output [1:0] RXDATAVALID; + output [1:0] RXSTARTOFSEQ; + output [1:0] TXBUFSTATUS; + output [2:0] RXBUFSTATUS; + output [2:0] RXHEADER; + output [2:0] RXSTATUS; + output [31:0] RXDATA; + output [3:0] RXCHARISCOMMA; + output [3:0] RXCHARISK; + output [3:0] RXCHBONDO; + output [3:0] RXDISPERR; + output [3:0] RXNOTINTABLE; + output [4:0] RXPHMONITOR; + output [4:0] RXPHSLIPMONITOR; + input CFGRESET; + input CLKRSVD0; + input CLKRSVD1; + input DMONFIFORESET; + input DMONITORCLK; + input DRPCLK; + input DRPEN; + input DRPWE; + input EYESCANMODE; + input EYESCANRESET; + input EYESCANTRIGGER; + input GTPRXN; + input GTPRXP; + input GTRESETSEL; + input GTRXRESET; + input GTTXRESET; + input PLL0CLK; + input PLL0REFCLK; + input PLL1CLK; + input PLL1REFCLK; + input PMARSVDIN0; + input PMARSVDIN1; + input PMARSVDIN2; + input PMARSVDIN3; + input PMARSVDIN4; + input RESETOVRD; + input RX8B10BEN; + input RXBUFRESET; + input RXCDRFREQRESET; + input RXCDRHOLD; + input RXCDROVRDEN; + input RXCDRRESET; + input RXCDRRESETRSV; + input RXCHBONDEN; + input RXCHBONDMASTER; + input RXCHBONDSLAVE; + input RXCOMMADETEN; + input RXDDIEN; + input RXDFEXYDEN; + input RXDLYBYPASS; + input RXDLYEN; + input RXDLYOVRDEN; + input RXDLYSRESET; + input RXGEARBOXSLIP; + input RXLPMHFHOLD; + input RXLPMHFOVRDEN; + input RXLPMLFHOLD; + input RXLPMLFOVRDEN; + input RXLPMOSINTNTRLEN; + input RXLPMRESET; + input RXMCOMMAALIGNEN; + input RXOOBRESET; + input RXOSCALRESET; + input RXOSHOLD; + input RXOSINTEN; + input RXOSINTHOLD; + input RXOSINTNTRLEN; + input RXOSINTOVRDEN; + input RXOSINTPD; + input RXOSINTSTROBE; + input RXOSINTTESTOVRDEN; + input RXOSOVRDEN; + input RXPCOMMAALIGNEN; + input RXPCSRESET; + input RXPHALIGN; + input RXPHALIGNEN; + input RXPHDLYPD; + input RXPHDLYRESET; + input RXPHOVRDEN; + input RXPMARESET; + input RXPOLARITY; + input RXPRBSCNTRESET; + input RXRATEMODE; + input RXSLIDE; + input RXSYNCALLIN; + input RXSYNCIN; + input RXSYNCMODE; + input RXUSERRDY; + input RXUSRCLK2; + input RXUSRCLK; + input SETERRSTATUS; + input SIGVALIDCLK; + input TX8B10BEN; + input TXCOMINIT; + input TXCOMSAS; + input TXCOMWAKE; + input TXDEEMPH; + input TXDETECTRX; + input TXDIFFPD; + input TXDLYBYPASS; + input TXDLYEN; + input TXDLYHOLD; + input TXDLYOVRDEN; + input TXDLYSRESET; + input TXDLYUPDOWN; + input TXELECIDLE; + input TXINHIBIT; + input TXPCSRESET; + input TXPDELECIDLEMODE; + input TXPHALIGN; + input TXPHALIGNEN; + input TXPHDLYPD; + input TXPHDLYRESET; + input TXPHDLYTSTCLK; + input TXPHINIT; + input TXPHOVRDEN; + input TXPIPPMEN; + input TXPIPPMOVRDEN; + input TXPIPPMPD; + input TXPIPPMSEL; + input TXPISOPD; + input TXPMARESET; + input TXPOLARITY; + input TXPOSTCURSORINV; + input TXPRBSFORCEERR; + input TXPRECURSORINV; + input TXRATEMODE; + input TXSTARTSEQ; + input TXSWING; + input TXSYNCALLIN; + input TXSYNCIN; + input TXSYNCMODE; + input TXUSERRDY; + input TXUSRCLK2; + input TXUSRCLK; + input [13:0] RXADAPTSELTEST; + input [15:0] DRPDI; + input [15:0] GTRSVD; + input [15:0] PCSRSVDIN; + input [19:0] TSTIN; + input [1:0] RXELECIDLEMODE; + input [1:0] RXPD; + input [1:0] RXSYSCLKSEL; + input [1:0] TXPD; + input [1:0] TXSYSCLKSEL; + input [2:0] LOOPBACK; + input [2:0] RXCHBONDLEVEL; + input [2:0] RXOUTCLKSEL; + input [2:0] RXPRBSSEL; + input [2:0] RXRATE; + input [2:0] TXBUFDIFFCTRL; + input [2:0] TXHEADER; + input [2:0] TXMARGIN; + input [2:0] TXOUTCLKSEL; + input [2:0] TXPRBSSEL; + input [2:0] TXRATE; + input [31:0] TXDATA; + input [3:0] RXCHBONDI; + input [3:0] RXOSINTCFG; + input [3:0] RXOSINTID0; + input [3:0] TX8B10BBYPASS; + input [3:0] TXCHARDISPMODE; + input [3:0] TXCHARDISPVAL; + input [3:0] TXCHARISK; + input [3:0] TXDIFFCTRL; + input [4:0] TXPIPPMSTEPSIZE; + input [4:0] TXPOSTCURSOR; + input [4:0] TXPRECURSOR; + input [6:0] TXMAINCURSOR; + input [6:0] TXSEQUENCE; + input [8:0] DRPADDR; +endmodule + +module GTPE2_COMMON (...); + parameter [63:0] BIAS_CFG = 64'h0000000000000000; + parameter [31:0] COMMON_CFG = 32'h00000000; + parameter [0:0] IS_DRPCLK_INVERTED = 1'b0; + parameter [0:0] IS_GTGREFCLK0_INVERTED = 1'b0; + parameter [0:0] IS_GTGREFCLK1_INVERTED = 1'b0; + parameter [0:0] IS_PLL0LOCKDETCLK_INVERTED = 1'b0; + parameter [0:0] IS_PLL1LOCKDETCLK_INVERTED = 1'b0; + parameter [26:0] PLL0_CFG = 27'h01F03DC; + parameter [0:0] PLL0_DMON_CFG = 1'b0; + parameter integer PLL0_FBDIV = 4; + parameter integer PLL0_FBDIV_45 = 5; + parameter [23:0] PLL0_INIT_CFG = 24'h00001E; + parameter [8:0] PLL0_LOCK_CFG = 9'h1E8; + parameter integer PLL0_REFCLK_DIV = 1; + parameter [26:0] PLL1_CFG = 27'h01F03DC; + parameter [0:0] PLL1_DMON_CFG = 1'b0; + parameter integer PLL1_FBDIV = 4; + parameter integer PLL1_FBDIV_45 = 5; + parameter [23:0] PLL1_INIT_CFG = 24'h00001E; + parameter [8:0] PLL1_LOCK_CFG = 9'h1E8; + parameter integer PLL1_REFCLK_DIV = 1; + parameter [7:0] PLL_CLKOUT_CFG = 8'b00000000; + parameter [15:0] RSVD_ATTR0 = 16'h0000; + parameter [15:0] RSVD_ATTR1 = 16'h0000; + parameter [2:0] SIM_PLL0REFCLK_SEL = 3'b001; + parameter [2:0] SIM_PLL1REFCLK_SEL = 3'b001; + parameter SIM_RESET_SPEEDUP = "TRUE"; + parameter SIM_VERSION = "1.0"; + output DRPRDY; + output PLL0FBCLKLOST; + output PLL0LOCK; + output PLL0OUTCLK; + output PLL0OUTREFCLK; + output PLL0REFCLKLOST; + output PLL1FBCLKLOST; + output PLL1LOCK; + output PLL1OUTCLK; + output PLL1OUTREFCLK; + output PLL1REFCLKLOST; + output REFCLKOUTMONITOR0; + output REFCLKOUTMONITOR1; + output [15:0] DRPDO; + output [15:0] PMARSVDOUT; + output [7:0] DMONITOROUT; + input BGBYPASSB; + input BGMONITORENB; + input BGPDB; + input BGRCALOVRDENB; + input DRPCLK; + input DRPEN; + input DRPWE; + input GTEASTREFCLK0; + input GTEASTREFCLK1; + input GTGREFCLK0; + input GTGREFCLK1; + input GTREFCLK0; + input GTREFCLK1; + input GTWESTREFCLK0; + input GTWESTREFCLK1; + input PLL0LOCKDETCLK; + input PLL0LOCKEN; + input PLL0PD; + input PLL0RESET; + input PLL1LOCKDETCLK; + input PLL1LOCKEN; + input PLL1PD; + input PLL1RESET; + input RCALENB; + input [15:0] DRPDI; + input [15:0] PLLRSVD1; + input [2:0] PLL0REFCLKSEL; + input [2:0] PLL1REFCLKSEL; + input [4:0] BGRCALOVRD; + input [4:0] PLLRSVD2; + input [7:0] DRPADDR; + input [7:0] PMARSVD; +endmodule + +module GTXE2_CHANNEL (...); + parameter ALIGN_COMMA_DOUBLE = "FALSE"; + parameter [9:0] ALIGN_COMMA_ENABLE = 10'b0001111111; + parameter integer ALIGN_COMMA_WORD = 1; + parameter ALIGN_MCOMMA_DET = "TRUE"; + parameter [9:0] ALIGN_MCOMMA_VALUE = 10'b1010000011; + parameter ALIGN_PCOMMA_DET = "TRUE"; + parameter [9:0] ALIGN_PCOMMA_VALUE = 10'b0101111100; + parameter CBCC_DATA_SOURCE_SEL = "DECODED"; + parameter CHAN_BOND_KEEP_ALIGN = "FALSE"; + parameter integer CHAN_BOND_MAX_SKEW = 7; + parameter [9:0] CHAN_BOND_SEQ_1_1 = 10'b0101111100; + parameter [9:0] CHAN_BOND_SEQ_1_2 = 10'b0000000000; + parameter [9:0] CHAN_BOND_SEQ_1_3 = 10'b0000000000; + parameter [9:0] CHAN_BOND_SEQ_1_4 = 10'b0000000000; + parameter [3:0] CHAN_BOND_SEQ_1_ENABLE = 4'b1111; + parameter [9:0] CHAN_BOND_SEQ_2_1 = 10'b0100000000; + parameter [9:0] CHAN_BOND_SEQ_2_2 = 10'b0100000000; + parameter [9:0] CHAN_BOND_SEQ_2_3 = 10'b0100000000; + parameter [9:0] CHAN_BOND_SEQ_2_4 = 10'b0100000000; + parameter [3:0] CHAN_BOND_SEQ_2_ENABLE = 4'b1111; + parameter CHAN_BOND_SEQ_2_USE = "FALSE"; + parameter integer CHAN_BOND_SEQ_LEN = 1; + parameter CLK_CORRECT_USE = "TRUE"; + parameter CLK_COR_KEEP_IDLE = "FALSE"; + parameter integer CLK_COR_MAX_LAT = 20; + parameter integer CLK_COR_MIN_LAT = 18; + parameter CLK_COR_PRECEDENCE = "TRUE"; + parameter integer CLK_COR_REPEAT_WAIT = 0; + parameter [9:0] CLK_COR_SEQ_1_1 = 10'b0100011100; + parameter [9:0] CLK_COR_SEQ_1_2 = 10'b0000000000; + parameter [9:0] CLK_COR_SEQ_1_3 = 10'b0000000000; + parameter [9:0] CLK_COR_SEQ_1_4 = 10'b0000000000; + parameter [3:0] CLK_COR_SEQ_1_ENABLE = 4'b1111; + parameter [9:0] CLK_COR_SEQ_2_1 = 10'b0100000000; + parameter [9:0] CLK_COR_SEQ_2_2 = 10'b0100000000; + parameter [9:0] CLK_COR_SEQ_2_3 = 10'b0100000000; + parameter [9:0] CLK_COR_SEQ_2_4 = 10'b0100000000; + parameter [3:0] CLK_COR_SEQ_2_ENABLE = 4'b1111; + parameter CLK_COR_SEQ_2_USE = "FALSE"; + parameter integer CLK_COR_SEQ_LEN = 1; + parameter [23:0] CPLL_CFG = 24'hB007D8; + parameter integer CPLL_FBDIV = 4; + parameter integer CPLL_FBDIV_45 = 5; + parameter [23:0] CPLL_INIT_CFG = 24'h00001E; + parameter [15:0] CPLL_LOCK_CFG = 16'h01E8; + parameter integer CPLL_REFCLK_DIV = 1; + parameter DEC_MCOMMA_DETECT = "TRUE"; + parameter DEC_PCOMMA_DETECT = "TRUE"; + parameter DEC_VALID_COMMA_ONLY = "TRUE"; + parameter [23:0] DMONITOR_CFG = 24'h000A00; + parameter [5:0] ES_CONTROL = 6'b000000; + parameter ES_ERRDET_EN = "FALSE"; + parameter ES_EYE_SCAN_EN = "FALSE"; + parameter [11:0] ES_HORZ_OFFSET = 12'h000; + parameter [9:0] ES_PMA_CFG = 10'b0000000000; + parameter [4:0] ES_PRESCALE = 5'b00000; + parameter [79:0] ES_QUALIFIER = 80'h00000000000000000000; + parameter [79:0] ES_QUAL_MASK = 80'h00000000000000000000; + parameter [79:0] ES_SDATA_MASK = 80'h00000000000000000000; + parameter [8:0] ES_VERT_OFFSET = 9'b000000000; + parameter [3:0] FTS_DESKEW_SEQ_ENABLE = 4'b1111; + parameter [3:0] FTS_LANE_DESKEW_CFG = 4'b1111; + parameter FTS_LANE_DESKEW_EN = "FALSE"; + parameter [2:0] GEARBOX_MODE = 3'b000; + parameter [0:0] IS_CPLLLOCKDETCLK_INVERTED = 1'b0; + parameter [0:0] IS_DRPCLK_INVERTED = 1'b0; + parameter [0:0] IS_GTGREFCLK_INVERTED = 1'b0; + parameter [0:0] IS_RXUSRCLK2_INVERTED = 1'b0; + parameter [0:0] IS_RXUSRCLK_INVERTED = 1'b0; + parameter [0:0] IS_TXPHDLYTSTCLK_INVERTED = 1'b0; + parameter [0:0] IS_TXUSRCLK2_INVERTED = 1'b0; + parameter [0:0] IS_TXUSRCLK_INVERTED = 1'b0; + parameter [1:0] OUTREFCLK_SEL_INV = 2'b11; + parameter PCS_PCIE_EN = "FALSE"; + parameter [47:0] PCS_RSVD_ATTR = 48'h000000000000; + parameter [11:0] PD_TRANS_TIME_FROM_P2 = 12'h03C; + parameter [7:0] PD_TRANS_TIME_NONE_P2 = 8'h19; + parameter [7:0] PD_TRANS_TIME_TO_P2 = 8'h64; + parameter [31:0] PMA_RSV = 32'h00000000; + parameter [15:0] PMA_RSV2 = 16'h2050; + parameter [1:0] PMA_RSV3 = 2'b00; + parameter [31:0] PMA_RSV4 = 32'h00000000; + parameter [4:0] RXBUFRESET_TIME = 5'b00001; + parameter RXBUF_ADDR_MODE = "FULL"; + parameter [3:0] RXBUF_EIDLE_HI_CNT = 4'b1000; + parameter [3:0] RXBUF_EIDLE_LO_CNT = 4'b0000; + parameter RXBUF_EN = "TRUE"; + parameter RXBUF_RESET_ON_CB_CHANGE = "TRUE"; + parameter RXBUF_RESET_ON_COMMAALIGN = "FALSE"; + parameter RXBUF_RESET_ON_EIDLE = "FALSE"; + parameter RXBUF_RESET_ON_RATE_CHANGE = "TRUE"; + parameter integer RXBUF_THRESH_OVFLW = 61; + parameter RXBUF_THRESH_OVRD = "FALSE"; + parameter integer RXBUF_THRESH_UNDFLW = 4; + parameter [4:0] RXCDRFREQRESET_TIME = 5'b00001; + parameter [4:0] RXCDRPHRESET_TIME = 5'b00001; + parameter [71:0] RXCDR_CFG = 72'h0B000023FF20400020; + parameter [0:0] RXCDR_FR_RESET_ON_EIDLE = 1'b0; + parameter [0:0] RXCDR_HOLD_DURING_EIDLE = 1'b0; + parameter [5:0] RXCDR_LOCK_CFG = 6'b010101; + parameter [0:0] RXCDR_PH_RESET_ON_EIDLE = 1'b0; + parameter [6:0] RXDFELPMRESET_TIME = 7'b0001111; + parameter [15:0] RXDLY_CFG = 16'h001F; + parameter [8:0] RXDLY_LCFG = 9'h030; + parameter [15:0] RXDLY_TAP_CFG = 16'h0000; + parameter RXGEARBOX_EN = "FALSE"; + parameter [4:0] RXISCANRESET_TIME = 5'b00001; + parameter [13:0] RXLPM_HF_CFG = 14'b00000011110000; + parameter [13:0] RXLPM_LF_CFG = 14'b00000011110000; + parameter [6:0] RXOOB_CFG = 7'b0000110; + parameter integer RXOUT_DIV = 2; + parameter [4:0] RXPCSRESET_TIME = 5'b00001; + parameter [23:0] RXPHDLY_CFG = 24'h084020; + parameter [23:0] RXPH_CFG = 24'h000000; + parameter [4:0] RXPH_MONITOR_SEL = 5'b00000; + parameter [4:0] RXPMARESET_TIME = 5'b00011; + parameter [0:0] RXPRBS_ERR_LOOPBACK = 1'b0; + parameter integer RXSLIDE_AUTO_WAIT = 7; + parameter RXSLIDE_MODE = "OFF"; + parameter [11:0] RX_BIAS_CFG = 12'b000000000000; + parameter [5:0] RX_BUFFER_CFG = 6'b000000; + parameter integer RX_CLK25_DIV = 7; + parameter [0:0] RX_CLKMUX_PD = 1'b1; + parameter [1:0] RX_CM_SEL = 2'b11; + parameter [2:0] RX_CM_TRIM = 3'b100; + parameter integer RX_DATA_WIDTH = 20; + parameter [5:0] RX_DDI_SEL = 6'b000000; + parameter [11:0] RX_DEBUG_CFG = 12'b000000000000; + parameter RX_DEFER_RESET_BUF_EN = "TRUE"; + parameter [22:0] RX_DFE_GAIN_CFG = 23'h180E0F; + parameter [11:0] RX_DFE_H2_CFG = 12'b000111100000; + parameter [11:0] RX_DFE_H3_CFG = 12'b000111100000; + parameter [10:0] RX_DFE_H4_CFG = 11'b00011110000; + parameter [10:0] RX_DFE_H5_CFG = 11'b00011110000; + parameter [12:0] RX_DFE_KL_CFG = 13'b0001111110000; + parameter [31:0] RX_DFE_KL_CFG2 = 32'h3008E56A; + parameter [15:0] RX_DFE_LPM_CFG = 16'h0904; + parameter [0:0] RX_DFE_LPM_HOLD_DURING_EIDLE = 1'b0; + parameter [16:0] RX_DFE_UT_CFG = 17'b00111111000000000; + parameter [16:0] RX_DFE_VP_CFG = 17'b00011111100000000; + parameter [12:0] RX_DFE_XYD_CFG = 13'b0000000010000; + parameter RX_DISPERR_SEQ_MATCH = "TRUE"; + parameter integer RX_INT_DATAWIDTH = 0; + parameter [12:0] RX_OS_CFG = 13'b0001111110000; + parameter integer RX_SIG_VALID_DLY = 10; + parameter RX_XCLK_SEL = "RXREC"; + parameter integer SAS_MAX_COM = 64; + parameter integer SAS_MIN_COM = 36; + parameter [3:0] SATA_BURST_SEQ_LEN = 4'b1111; + parameter [2:0] SATA_BURST_VAL = 3'b100; + parameter SATA_CPLL_CFG = "VCO_3000MHZ"; + parameter [2:0] SATA_EIDLE_VAL = 3'b100; + parameter integer SATA_MAX_BURST = 8; + parameter integer SATA_MAX_INIT = 21; + parameter integer SATA_MAX_WAKE = 7; + parameter integer SATA_MIN_BURST = 4; + parameter integer SATA_MIN_INIT = 12; + parameter integer SATA_MIN_WAKE = 4; + parameter SHOW_REALIGN_COMMA = "TRUE"; + parameter [2:0] SIM_CPLLREFCLK_SEL = 3'b001; + parameter SIM_RECEIVER_DETECT_PASS = "TRUE"; + parameter SIM_RESET_SPEEDUP = "TRUE"; + parameter SIM_TX_EIDLE_DRIVE_LEVEL = "X"; + parameter SIM_VERSION = "4.0"; + parameter [4:0] TERM_RCAL_CFG = 5'b10000; + parameter [0:0] TERM_RCAL_OVRD = 1'b0; + parameter [7:0] TRANS_TIME_RATE = 8'h0E; + parameter [31:0] TST_RSV = 32'h00000000; + parameter TXBUF_EN = "TRUE"; + parameter TXBUF_RESET_ON_RATE_CHANGE = "FALSE"; + parameter [15:0] TXDLY_CFG = 16'h001F; + parameter [8:0] TXDLY_LCFG = 9'h030; + parameter [15:0] TXDLY_TAP_CFG = 16'h0000; + parameter TXGEARBOX_EN = "FALSE"; + parameter integer TXOUT_DIV = 2; + parameter [4:0] TXPCSRESET_TIME = 5'b00001; + parameter [23:0] TXPHDLY_CFG = 24'h084020; + parameter [15:0] TXPH_CFG = 16'h0780; + parameter [4:0] TXPH_MONITOR_SEL = 5'b00000; + parameter [4:0] TXPMARESET_TIME = 5'b00001; + parameter integer TX_CLK25_DIV = 7; + parameter [0:0] TX_CLKMUX_PD = 1'b1; + parameter integer TX_DATA_WIDTH = 20; + parameter [4:0] TX_DEEMPH0 = 5'b00000; + parameter [4:0] TX_DEEMPH1 = 5'b00000; + parameter TX_DRIVE_MODE = "DIRECT"; + parameter [2:0] TX_EIDLE_ASSERT_DELAY = 3'b110; + parameter [2:0] TX_EIDLE_DEASSERT_DELAY = 3'b100; + parameter integer TX_INT_DATAWIDTH = 0; + parameter TX_LOOPBACK_DRIVE_HIZ = "FALSE"; + parameter [0:0] TX_MAINCURSOR_SEL = 1'b0; + parameter [6:0] TX_MARGIN_FULL_0 = 7'b1001110; + parameter [6:0] TX_MARGIN_FULL_1 = 7'b1001001; + parameter [6:0] TX_MARGIN_FULL_2 = 7'b1000101; + parameter [6:0] TX_MARGIN_FULL_3 = 7'b1000010; + parameter [6:0] TX_MARGIN_FULL_4 = 7'b1000000; + parameter [6:0] TX_MARGIN_LOW_0 = 7'b1000110; + parameter [6:0] TX_MARGIN_LOW_1 = 7'b1000100; + parameter [6:0] TX_MARGIN_LOW_2 = 7'b1000010; + parameter [6:0] TX_MARGIN_LOW_3 = 7'b1000000; + parameter [6:0] TX_MARGIN_LOW_4 = 7'b1000000; + parameter [0:0] TX_PREDRIVER_MODE = 1'b0; + parameter [0:0] TX_QPI_STATUS_EN = 1'b0; + parameter [13:0] TX_RXDETECT_CFG = 14'h1832; + parameter [2:0] TX_RXDETECT_REF = 3'b100; + parameter TX_XCLK_SEL = "TXUSR"; + parameter [0:0] UCODEER_CLR = 1'b0; + output CPLLFBCLKLOST; + output CPLLLOCK; + output CPLLREFCLKLOST; + output DRPRDY; + output EYESCANDATAERROR; + output GTREFCLKMONITOR; + output GTXTXN; + output GTXTXP; + output PHYSTATUS; + output RXBYTEISALIGNED; + output RXBYTEREALIGN; + output RXCDRLOCK; + output RXCHANBONDSEQ; + output RXCHANISALIGNED; + output RXCHANREALIGN; + output RXCOMINITDET; + output RXCOMMADET; + output RXCOMSASDET; + output RXCOMWAKEDET; + output RXDATAVALID; + output RXDLYSRESETDONE; + output RXELECIDLE; + output RXHEADERVALID; + output RXOUTCLK; + output RXOUTCLKFABRIC; + output RXOUTCLKPCS; + output RXPHALIGNDONE; + output RXPRBSERR; + output RXQPISENN; + output RXQPISENP; + output RXRATEDONE; + output RXRESETDONE; + output RXSTARTOFSEQ; + output RXVALID; + output TXCOMFINISH; + output TXDLYSRESETDONE; + output TXGEARBOXREADY; + output TXOUTCLK; + output TXOUTCLKFABRIC; + output TXOUTCLKPCS; + output TXPHALIGNDONE; + output TXPHINITDONE; + output TXQPISENN; + output TXQPISENP; + output TXRATEDONE; + output TXRESETDONE; + output [15:0] DRPDO; + output [15:0] PCSRSVDOUT; + output [1:0] RXCLKCORCNT; + output [1:0] TXBUFSTATUS; + output [2:0] RXBUFSTATUS; + output [2:0] RXHEADER; + output [2:0] RXSTATUS; + output [4:0] RXCHBONDO; + output [4:0] RXPHMONITOR; + output [4:0] RXPHSLIPMONITOR; + output [63:0] RXDATA; + output [6:0] RXMONITOROUT; + output [7:0] DMONITOROUT; + output [7:0] RXCHARISCOMMA; + output [7:0] RXCHARISK; + output [7:0] RXDISPERR; + output [7:0] RXNOTINTABLE; + output [9:0] TSTOUT; + input CFGRESET; + input CPLLLOCKDETCLK; + input CPLLLOCKEN; + input CPLLPD; + input CPLLRESET; + input DRPCLK; + input DRPEN; + input DRPWE; + input EYESCANMODE; + input EYESCANRESET; + input EYESCANTRIGGER; + input GTGREFCLK; + input GTNORTHREFCLK0; + input GTNORTHREFCLK1; + input GTREFCLK0; + input GTREFCLK1; + input GTRESETSEL; + input GTRXRESET; + input GTSOUTHREFCLK0; + input GTSOUTHREFCLK1; + input GTTXRESET; + input GTXRXN; + input GTXRXP; + input QPLLCLK; + input QPLLREFCLK; + input RESETOVRD; + input RX8B10BEN; + input RXBUFRESET; + input RXCDRFREQRESET; + input RXCDRHOLD; + input RXCDROVRDEN; + input RXCDRRESET; + input RXCDRRESETRSV; + input RXCHBONDEN; + input RXCHBONDMASTER; + input RXCHBONDSLAVE; + input RXCOMMADETEN; + input RXDDIEN; + input RXDFEAGCHOLD; + input RXDFEAGCOVRDEN; + input RXDFECM1EN; + input RXDFELFHOLD; + input RXDFELFOVRDEN; + input RXDFELPMRESET; + input RXDFETAP2HOLD; + input RXDFETAP2OVRDEN; + input RXDFETAP3HOLD; + input RXDFETAP3OVRDEN; + input RXDFETAP4HOLD; + input RXDFETAP4OVRDEN; + input RXDFETAP5HOLD; + input RXDFETAP5OVRDEN; + input RXDFEUTHOLD; + input RXDFEUTOVRDEN; + input RXDFEVPHOLD; + input RXDFEVPOVRDEN; + input RXDFEVSEN; + input RXDFEXYDEN; + input RXDFEXYDHOLD; + input RXDFEXYDOVRDEN; + input RXDLYBYPASS; + input RXDLYEN; + input RXDLYOVRDEN; + input RXDLYSRESET; + input RXGEARBOXSLIP; + input RXLPMEN; + input RXLPMHFHOLD; + input RXLPMHFOVRDEN; + input RXLPMLFHOLD; + input RXLPMLFKLOVRDEN; + input RXMCOMMAALIGNEN; + input RXOOBRESET; + input RXOSHOLD; + input RXOSOVRDEN; + input RXPCOMMAALIGNEN; + input RXPCSRESET; + input RXPHALIGN; + input RXPHALIGNEN; + input RXPHDLYPD; + input RXPHDLYRESET; + input RXPHOVRDEN; + input RXPMARESET; + input RXPOLARITY; + input RXPRBSCNTRESET; + input RXQPIEN; + input RXSLIDE; + input RXUSERRDY; + input RXUSRCLK2; + input RXUSRCLK; + input SETERRSTATUS; + input TX8B10BEN; + input TXCOMINIT; + input TXCOMSAS; + input TXCOMWAKE; + input TXDEEMPH; + input TXDETECTRX; + input TXDIFFPD; + input TXDLYBYPASS; + input TXDLYEN; + input TXDLYHOLD; + input TXDLYOVRDEN; + input TXDLYSRESET; + input TXDLYUPDOWN; + input TXELECIDLE; + input TXINHIBIT; + input TXPCSRESET; + input TXPDELECIDLEMODE; + input TXPHALIGN; + input TXPHALIGNEN; + input TXPHDLYPD; + input TXPHDLYRESET; + input TXPHDLYTSTCLK; + input TXPHINIT; + input TXPHOVRDEN; + input TXPISOPD; + input TXPMARESET; + input TXPOLARITY; + input TXPOSTCURSORINV; + input TXPRBSFORCEERR; + input TXPRECURSORINV; + input TXQPIBIASEN; + input TXQPISTRONGPDOWN; + input TXQPIWEAKPUP; + input TXSTARTSEQ; + input TXSWING; + input TXUSERRDY; + input TXUSRCLK2; + input TXUSRCLK; + input [15:0] DRPDI; + input [15:0] GTRSVD; + input [15:0] PCSRSVDIN; + input [19:0] TSTIN; + input [1:0] RXELECIDLEMODE; + input [1:0] RXMONITORSEL; + input [1:0] RXPD; + input [1:0] RXSYSCLKSEL; + input [1:0] TXPD; + input [1:0] TXSYSCLKSEL; + input [2:0] CPLLREFCLKSEL; + input [2:0] LOOPBACK; + input [2:0] RXCHBONDLEVEL; + input [2:0] RXOUTCLKSEL; + input [2:0] RXPRBSSEL; + input [2:0] RXRATE; + input [2:0] TXBUFDIFFCTRL; + input [2:0] TXHEADER; + input [2:0] TXMARGIN; + input [2:0] TXOUTCLKSEL; + input [2:0] TXPRBSSEL; + input [2:0] TXRATE; + input [3:0] CLKRSVD; + input [3:0] TXDIFFCTRL; + input [4:0] PCSRSVDIN2; + input [4:0] PMARSVDIN2; + input [4:0] PMARSVDIN; + input [4:0] RXCHBONDI; + input [4:0] TXPOSTCURSOR; + input [4:0] TXPRECURSOR; + input [63:0] TXDATA; + input [6:0] TXMAINCURSOR; + input [6:0] TXSEQUENCE; + input [7:0] TX8B10BBYPASS; + input [7:0] TXCHARDISPMODE; + input [7:0] TXCHARDISPVAL; + input [7:0] TXCHARISK; + input [8:0] DRPADDR; +endmodule + +module GTXE2_COMMON (...); + parameter [63:0] BIAS_CFG = 64'h0000040000001000; + parameter [31:0] COMMON_CFG = 32'h00000000; + parameter [0:0] IS_DRPCLK_INVERTED = 1'b0; + parameter [0:0] IS_GTGREFCLK_INVERTED = 1'b0; + parameter [0:0] IS_QPLLLOCKDETCLK_INVERTED = 1'b0; + parameter [26:0] QPLL_CFG = 27'h0680181; + parameter [3:0] QPLL_CLKOUT_CFG = 4'b0000; + parameter [5:0] QPLL_COARSE_FREQ_OVRD = 6'b010000; + parameter [0:0] QPLL_COARSE_FREQ_OVRD_EN = 1'b0; + parameter [9:0] QPLL_CP = 10'b0000011111; + parameter [0:0] QPLL_CP_MONITOR_EN = 1'b0; + parameter [0:0] QPLL_DMONITOR_SEL = 1'b0; + parameter [9:0] QPLL_FBDIV = 10'b0000000000; + parameter [0:0] QPLL_FBDIV_MONITOR_EN = 1'b0; + parameter [0:0] QPLL_FBDIV_RATIO = 1'b0; + parameter [23:0] QPLL_INIT_CFG = 24'h000006; + parameter [15:0] QPLL_LOCK_CFG = 16'h21E8; + parameter [3:0] QPLL_LPF = 4'b1111; + parameter integer QPLL_REFCLK_DIV = 2; + parameter [2:0] SIM_QPLLREFCLK_SEL = 3'b001; + parameter SIM_RESET_SPEEDUP = "TRUE"; + parameter SIM_VERSION = "4.0"; + output DRPRDY; + output QPLLFBCLKLOST; + output QPLLLOCK; + output QPLLOUTCLK; + output QPLLOUTREFCLK; + output QPLLREFCLKLOST; + output REFCLKOUTMONITOR; + output [15:0] DRPDO; + output [7:0] QPLLDMONITOR; + input BGBYPASSB; + input BGMONITORENB; + input BGPDB; + input DRPCLK; + input DRPEN; + input DRPWE; + input GTGREFCLK; + input GTNORTHREFCLK0; + input GTNORTHREFCLK1; + input GTREFCLK0; + input GTREFCLK1; + input GTSOUTHREFCLK0; + input GTSOUTHREFCLK1; + input QPLLLOCKDETCLK; + input QPLLLOCKEN; + input QPLLOUTRESET; + input QPLLPD; + input QPLLRESET; + input RCALENB; + input [15:0] DRPDI; + input [15:0] QPLLRSVD1; + input [2:0] QPLLREFCLKSEL; + input [4:0] BGRCALOVRD; + input [4:0] QPLLRSVD2; + input [7:0] DRPADDR; + input [7:0] PMARSVD; +endmodule + +module IBUF_IBUFDISABLE (...); + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + parameter SIM_DEVICE = "7SERIES"; + parameter USE_IBUFDISABLE = "TRUE"; + output O; + input I; + input IBUFDISABLE; +endmodule + +module IBUF_INTERMDISABLE (...); + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + parameter SIM_DEVICE = "7SERIES"; + parameter USE_IBUFDISABLE = "TRUE"; + output O; + input I; + input IBUFDISABLE; + input INTERMDISABLE; +endmodule + +module IBUFDS (...); + parameter CAPACITANCE = "DONT_CARE"; + parameter DIFF_TERM = "FALSE"; + parameter DQS_BIAS = "FALSE"; + parameter IBUF_DELAY_VALUE = "0"; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IFD_DELAY_VALUE = "AUTO"; + parameter IOSTANDARD = "DEFAULT"; + output O; + input I, IB; +endmodule + +module IBUFDS_DIFF_OUT (...); + parameter DIFF_TERM = "FALSE"; + parameter DQS_BIAS = "FALSE"; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + output O, OB; + input I, IB; +endmodule + +module IBUFDS_DIFF_OUT_IBUFDISABLE (...); + parameter DIFF_TERM = "FALSE"; + parameter DQS_BIAS = "FALSE"; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + parameter SIM_DEVICE = "7SERIES"; + parameter USE_IBUFDISABLE = "TRUE"; + output O; + output OB; + input I; + input IB; + input IBUFDISABLE; +endmodule + +module IBUFDS_DIFF_OUT_INTERMDISABLE (...); + parameter DIFF_TERM = "FALSE"; + parameter DQS_BIAS = "FALSE"; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + parameter SIM_DEVICE = "7SERIES"; + parameter USE_IBUFDISABLE = "TRUE"; + output O; + output OB; + input I; + input IB; + input IBUFDISABLE; + input INTERMDISABLE; +endmodule + +module IBUFDS_GTE2 (...); + parameter CLKCM_CFG = "TRUE"; + parameter CLKRCV_TRST = "TRUE"; + parameter CLKSWING_CFG = "TRUE"; + output O; + output ODIV2; + input CEB; + input I; + input IB; +endmodule + +module IBUFDS_IBUFDISABLE (...); + parameter DIFF_TERM = "FALSE"; + parameter DQS_BIAS = "FALSE"; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + parameter SIM_DEVICE = "7SERIES"; + parameter USE_IBUFDISABLE = "TRUE"; + output O; + input I; + input IB; + input IBUFDISABLE; +endmodule + +module IBUFDS_INTERMDISABLE (...); + parameter DIFF_TERM = "FALSE"; + parameter DQS_BIAS = "FALSE"; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + parameter SIM_DEVICE = "7SERIES"; + parameter USE_IBUFDISABLE = "TRUE"; + output O; + input I; + input IB; + input IBUFDISABLE; + input INTERMDISABLE; +endmodule + +module ICAPE2 (...); + parameter [31:0] DEVICE_ID = 32'h04244093; + parameter ICAP_WIDTH = "X32"; + parameter SIM_CFG_FILE_NAME = "NONE"; + output [31:0] O; + input CLK; + input CSIB; + input RDWRB; + input [31:0] I; +endmodule + +module IDDR (...); + parameter DDR_CLK_EDGE = "OPPOSITE_EDGE"; + parameter INIT_Q1 = 1'b0; + parameter INIT_Q2 = 1'b0; + parameter [0:0] IS_C_INVERTED = 1'b0; + parameter [0:0] IS_D_INVERTED = 1'b0; + parameter SRTYPE = "SYNC"; + parameter MSGON = "TRUE"; + parameter XON = "TRUE"; + output Q1; + output Q2; + input C; + input CE; + input D; + input R; + input S; +endmodule + +module IDDR_2CLK (...); + parameter DDR_CLK_EDGE = "OPPOSITE_EDGE"; + parameter INIT_Q1 = 1'b0; + parameter INIT_Q2 = 1'b0; + parameter [0:0] IS_CB_INVERTED = 1'b0; + parameter [0:0] IS_C_INVERTED = 1'b0; + parameter [0:0] IS_D_INVERTED = 1'b0; + parameter SRTYPE = "SYNC"; + output Q1; + output Q2; + input C; + input CB; + input CE; + input D; + input R; + input S; +endmodule + +module IDELAYCTRL (...); + parameter SIM_DEVICE = "7SERIES"; + output RDY; + input REFCLK; + input RST; +endmodule + +module IDELAYE2 (...); + parameter CINVCTRL_SEL = "FALSE"; + parameter DELAY_SRC = "IDATAIN"; + parameter HIGH_PERFORMANCE_MODE = "FALSE"; + parameter IDELAY_TYPE = "FIXED"; + parameter integer IDELAY_VALUE = 0; + parameter [0:0] IS_C_INVERTED = 1'b0; + parameter [0:0] IS_DATAIN_INVERTED = 1'b0; + parameter [0:0] IS_IDATAIN_INVERTED = 1'b0; + parameter PIPE_SEL = "FALSE"; + parameter real REFCLK_FREQUENCY = 200.0; + parameter SIGNAL_PATTERN = "DATA"; + parameter integer SIM_DELAY_D = 0; + output [4:0] CNTVALUEOUT; + output DATAOUT; + input C; + input CE; + input CINVCTRL; + input [4:0] CNTVALUEIN; + input DATAIN; + input IDATAIN; + input INC; + input LD; + input LDPIPEEN; + input REGRST; +endmodule + +module IN_FIFO (...); + parameter integer ALMOST_EMPTY_VALUE = 1; + parameter integer ALMOST_FULL_VALUE = 1; + parameter ARRAY_MODE = "ARRAY_MODE_4_X_8"; + parameter SYNCHRONOUS_MODE = "FALSE"; + output ALMOSTEMPTY; + output ALMOSTFULL; + output EMPTY; + output FULL; + output [7:0] Q0; + output [7:0] Q1; + output [7:0] Q2; + output [7:0] Q3; + output [7:0] Q4; + output [7:0] Q5; + output [7:0] Q6; + output [7:0] Q7; + output [7:0] Q8; + output [7:0] Q9; + input RDCLK; + input RDEN; + input RESET; + input WRCLK; + input WREN; + input [3:0] D0; + input [3:0] D1; + input [3:0] D2; + input [3:0] D3; + input [3:0] D4; + input [3:0] D7; + input [3:0] D8; + input [3:0] D9; + input [7:0] D5; + input [7:0] D6; +endmodule + +module IOBUF (...); + parameter integer DRIVE = 12; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + parameter SLEW = "SLOW"; + output O; + input I, T; +endmodule + +module IOBUF_DCIEN (...); + parameter integer DRIVE = 12; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + parameter SIM_DEVICE = "7SERIES"; + parameter SLEW = "SLOW"; + parameter USE_IBUFDISABLE = "TRUE"; + output O; + input DCITERMDISABLE; + input I; + input IBUFDISABLE; + input T; +endmodule + +module IOBUF_INTERMDISABLE (...); + parameter integer DRIVE = 12; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + parameter SIM_DEVICE = "7SERIES"; + parameter SLEW = "SLOW"; + parameter USE_IBUFDISABLE = "TRUE"; + output O; + input I; + input IBUFDISABLE; + input INTERMDISABLE; + input T; +endmodule + +module IOBUFDS (...); + parameter DIFF_TERM = "FALSE"; + parameter DQS_BIAS = "FALSE"; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + parameter SLEW = "SLOW"; + output O; + input I, T; +endmodule + +module IOBUFDS_DCIEN (...); + parameter DIFF_TERM = "FALSE"; + parameter DQS_BIAS = "FALSE"; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + parameter SIM_DEVICE = "7SERIES"; + parameter SLEW = "SLOW"; + parameter USE_IBUFDISABLE = "TRUE"; + output O; + input DCITERMDISABLE; + input I; + input IBUFDISABLE; + input T; +endmodule + +module IOBUFDS_DIFF_OUT (...); + parameter DIFF_TERM = "FALSE"; + parameter DQS_BIAS = "FALSE"; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + output O; + output OB; + input I; + input TM; + input TS; +endmodule + +module IOBUFDS_DIFF_OUT_DCIEN (...); + parameter DIFF_TERM = "FALSE"; + parameter DQS_BIAS = "FALSE"; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + parameter SIM_DEVICE = "7SERIES"; + parameter USE_IBUFDISABLE = "TRUE"; + output O; + output OB; + input DCITERMDISABLE; + input I; + input IBUFDISABLE; + input TM; + input TS; +endmodule + +module IOBUFDS_DIFF_OUT_INTERMDISABLE (...); + parameter DIFF_TERM = "FALSE"; + parameter DQS_BIAS = "FALSE"; + parameter IBUF_LOW_PWR = "TRUE"; + parameter IOSTANDARD = "DEFAULT"; + parameter SIM_DEVICE = "7SERIES"; + parameter USE_IBUFDISABLE = "TRUE"; + output O; + output OB; + input I; + input IBUFDISABLE; + input INTERMDISABLE; + input TM; + input TS; +endmodule + +module ISERDESE2 (...); + parameter DATA_RATE = "DDR"; + parameter integer DATA_WIDTH = 4; + parameter DYN_CLKDIV_INV_EN = "FALSE"; + parameter DYN_CLK_INV_EN = "FALSE"; + parameter [0:0] INIT_Q1 = 1'b0; + parameter [0:0] INIT_Q2 = 1'b0; + parameter [0:0] INIT_Q3 = 1'b0; + parameter [0:0] INIT_Q4 = 1'b0; + parameter INTERFACE_TYPE = "MEMORY"; + parameter IOBDELAY = "NONE"; + parameter [0:0] IS_CLKB_INVERTED = 1'b0; + parameter [0:0] IS_CLKDIVP_INVERTED = 1'b0; + parameter [0:0] IS_CLKDIV_INVERTED = 1'b0; + parameter [0:0] IS_CLK_INVERTED = 1'b0; + parameter [0:0] IS_D_INVERTED = 1'b0; + parameter [0:0] IS_OCLKB_INVERTED = 1'b0; + parameter [0:0] IS_OCLK_INVERTED = 1'b0; + parameter integer NUM_CE = 2; + parameter OFB_USED = "FALSE"; + parameter SERDES_MODE = "MASTER"; + parameter [0:0] SRVAL_Q1 = 1'b0; + parameter [0:0] SRVAL_Q2 = 1'b0; + parameter [0:0] SRVAL_Q3 = 1'b0; + parameter [0:0] SRVAL_Q4 = 1'b0; + output O; + output Q1; + output Q2; + output Q3; + output Q4; + output Q5; + output Q6; + output Q7; + output Q8; + output SHIFTOUT1; + output SHIFTOUT2; + input BITSLIP; + input CE1; + input CE2; + input CLK; + input CLKB; + input CLKDIV; + input CLKDIVP; + input D; + input DDLY; + input DYNCLKDIVSEL; + input DYNCLKSEL; + input OCLK; + input OCLKB; + input OFB; + input RST; + input SHIFTIN1; + input SHIFTIN2; +endmodule + +module KEEPER (...); +endmodule + +module LDCE (...); + parameter [0:0] INIT = 1'b0; + parameter [0:0] IS_CLR_INVERTED = 1'b0; + parameter [0:0] IS_G_INVERTED = 1'b0; + parameter MSGON = "TRUE"; + parameter XON = "TRUE"; + output Q; + input CLR, D, G, GE; +endmodule + +module LDPE (...); + parameter [0:0] INIT = 1'b1; + parameter [0:0] IS_G_INVERTED = 1'b0; + parameter [0:0] IS_PRE_INVERTED = 1'b0; + parameter MSGON = "TRUE"; + parameter XON = "TRUE"; + output Q; + input D, G, GE, PRE; +endmodule + +module LUT6_2 (...); + parameter [63:0] INIT = 64'h0000000000000000; + input I0, I1, I2, I3, I4, I5; + output O5, O6; +endmodule + +module MMCME2_ADV (...); + parameter BANDWIDTH = "OPTIMIZED"; + parameter real CLKFBOUT_MULT_F = 5.000; + parameter real CLKFBOUT_PHASE = 0.000; + parameter CLKFBOUT_USE_FINE_PS = "FALSE"; + parameter real CLKIN1_PERIOD = 0.000; + parameter real CLKIN2_PERIOD = 0.000; + parameter real CLKIN_FREQ_MAX = 1066.000; + parameter real CLKIN_FREQ_MIN = 10.000; + parameter real CLKOUT0_DIVIDE_F = 1.000; + parameter real CLKOUT0_DUTY_CYCLE = 0.500; + parameter real CLKOUT0_PHASE = 0.000; + parameter CLKOUT0_USE_FINE_PS = "FALSE"; + parameter integer CLKOUT1_DIVIDE = 1; + parameter real CLKOUT1_DUTY_CYCLE = 0.500; + parameter real CLKOUT1_PHASE = 0.000; + parameter CLKOUT1_USE_FINE_PS = "FALSE"; + parameter integer CLKOUT2_DIVIDE = 1; + parameter real CLKOUT2_DUTY_CYCLE = 0.500; + parameter real CLKOUT2_PHASE = 0.000; + parameter CLKOUT2_USE_FINE_PS = "FALSE"; + parameter integer CLKOUT3_DIVIDE = 1; + parameter real CLKOUT3_DUTY_CYCLE = 0.500; + parameter real CLKOUT3_PHASE = 0.000; + parameter CLKOUT3_USE_FINE_PS = "FALSE"; + parameter CLKOUT4_CASCADE = "FALSE"; + parameter integer CLKOUT4_DIVIDE = 1; + parameter real CLKOUT4_DUTY_CYCLE = 0.500; + parameter real CLKOUT4_PHASE = 0.000; + parameter CLKOUT4_USE_FINE_PS = "FALSE"; + parameter integer CLKOUT5_DIVIDE = 1; + parameter real CLKOUT5_DUTY_CYCLE = 0.500; + parameter real CLKOUT5_PHASE = 0.000; + parameter CLKOUT5_USE_FINE_PS = "FALSE"; + parameter integer CLKOUT6_DIVIDE = 1; + parameter real CLKOUT6_DUTY_CYCLE = 0.500; + parameter real CLKOUT6_PHASE = 0.000; + parameter CLKOUT6_USE_FINE_PS = "FALSE"; + parameter real CLKPFD_FREQ_MAX = 550.000; + parameter real CLKPFD_FREQ_MIN = 10.000; + parameter COMPENSATION = "ZHOLD"; + parameter integer DIVCLK_DIVIDE = 1; + parameter [0:0] IS_CLKINSEL_INVERTED = 1'b0; + parameter [0:0] IS_PSEN_INVERTED = 1'b0; + parameter [0:0] IS_PSINCDEC_INVERTED = 1'b0; + parameter [0:0] IS_PWRDWN_INVERTED = 1'b0; + parameter [0:0] IS_RST_INVERTED = 1'b0; + parameter real REF_JITTER1 = 0.010; + parameter real REF_JITTER2 = 0.010; + parameter SS_EN = "FALSE"; + parameter SS_MODE = "CENTER_HIGH"; + parameter integer SS_MOD_PERIOD = 10000; + parameter STARTUP_WAIT = "FALSE"; + parameter real VCOCLK_FREQ_MAX = 1600.000; + parameter real VCOCLK_FREQ_MIN = 600.000; + parameter STARTUP_WAIT = "FALSE"; + output CLKFBOUT; + output CLKFBOUTB; + output CLKFBSTOPPED; + output CLKINSTOPPED; + output CLKOUT0; + output CLKOUT0B; + output CLKOUT1; + output CLKOUT1B; + output CLKOUT2; + output CLKOUT2B; + output CLKOUT3; + output CLKOUT3B; + output CLKOUT4; + output CLKOUT5; + output CLKOUT6; + output [15:0] DO; + output DRDY; + output LOCKED; + output PSDONE; + input CLKFBIN; + input CLKIN1; + input CLKIN2; + input CLKINSEL; + input [6:0] DADDR; + input DCLK; + input DEN; + input [15:0] DI; + input DWE; + input PSCLK; + input PSEN; + input PSINCDEC; + input PWRDWN; + input RST; +endmodule + +module MMCME2_BASE (...); + parameter BANDWIDTH = "OPTIMIZED"; + parameter real CLKFBOUT_MULT_F = 5.000; + parameter real CLKFBOUT_PHASE = 0.000; + parameter real CLKIN1_PERIOD = 0.000; + parameter real CLKOUT0_DIVIDE_F = 1.000; + parameter real CLKOUT0_DUTY_CYCLE = 0.500; + parameter real CLKOUT0_PHASE = 0.000; + parameter integer CLKOUT1_DIVIDE = 1; + parameter real CLKOUT1_DUTY_CYCLE = 0.500; + parameter real CLKOUT1_PHASE = 0.000; + parameter integer CLKOUT2_DIVIDE = 1; + parameter real CLKOUT2_DUTY_CYCLE = 0.500; + parameter real CLKOUT2_PHASE = 0.000; + parameter integer CLKOUT3_DIVIDE = 1; + parameter real CLKOUT3_DUTY_CYCLE = 0.500; + parameter real CLKOUT3_PHASE = 0.000; + parameter CLKOUT4_CASCADE = "FALSE"; + parameter integer CLKOUT4_DIVIDE = 1; + parameter real CLKOUT4_DUTY_CYCLE = 0.500; + parameter real CLKOUT4_PHASE = 0.000; + parameter integer CLKOUT5_DIVIDE = 1; + parameter real CLKOUT5_DUTY_CYCLE = 0.500; + parameter real CLKOUT5_PHASE = 0.000; + parameter integer CLKOUT6_DIVIDE = 1; + parameter real CLKOUT6_DUTY_CYCLE = 0.500; + parameter real CLKOUT6_PHASE = 0.000; + parameter integer DIVCLK_DIVIDE = 1; + parameter real REF_JITTER1 = 0.010; + parameter STARTUP_WAIT = "FALSE"; + output CLKFBOUT; + output CLKFBOUTB; + output CLKOUT0; + output CLKOUT0B; + output CLKOUT1; + output CLKOUT1B; + output CLKOUT2; + output CLKOUT2B; + output CLKOUT3; + output CLKOUT3B; + output CLKOUT4; + output CLKOUT5; + output CLKOUT6; + output LOCKED; + input CLKFBIN; + input CLKIN1; + input PWRDWN; + input RST; +endmodule + +module OBUFDS (...); + parameter CAPACITANCE = "DONT_CARE"; + parameter IOSTANDARD = "DEFAULT"; + parameter SLEW = "SLOW"; + output O, OB; + input I; +endmodule + +module OBUFT (...); + parameter CAPACITANCE = "DONT_CARE"; + parameter integer DRIVE = 12; + parameter IOSTANDARD = "DEFAULT"; + parameter SLEW = "SLOW"; + output O; + input I, T; +endmodule + +module OBUFTDS (...); + parameter CAPACITANCE = "DONT_CARE"; + parameter IOSTANDARD = "DEFAULT"; + parameter SLEW = "SLOW"; + output O, OB; + input I, T; +endmodule + +module ODDR (...); + output Q; + input C; + input CE; + input D1; + input D2; + input R; + input S; + parameter DDR_CLK_EDGE = "OPPOSITE_EDGE"; + parameter INIT = 1'b0; + parameter [0:0] IS_C_INVERTED = 1'b0; + parameter [0:0] IS_D1_INVERTED = 1'b0; + parameter [0:0] IS_D2_INVERTED = 1'b0; + parameter SRTYPE = "SYNC"; + parameter MSGON = "TRUE"; + parameter XON = "TRUE"; +endmodule + +module ODELAYE2 (...); + parameter CINVCTRL_SEL = "FALSE"; + parameter DELAY_SRC = "ODATAIN"; + parameter HIGH_PERFORMANCE_MODE = "FALSE"; + parameter [0:0] IS_C_INVERTED = 1'b0; + parameter [0:0] IS_ODATAIN_INVERTED = 1'b0; + parameter ODELAY_TYPE = "FIXED"; + parameter integer ODELAY_VALUE = 0; + parameter PIPE_SEL = "FALSE"; + parameter real REFCLK_FREQUENCY = 200.0; + parameter SIGNAL_PATTERN = "DATA"; + parameter integer SIM_DELAY_D = 0; + output [4:0] CNTVALUEOUT; + output DATAOUT; + input C; + input CE; + input CINVCTRL; + input CLKIN; + input [4:0] CNTVALUEIN; + input INC; + input LD; + input LDPIPEEN; + input ODATAIN; + input REGRST; +endmodule + +module OSERDESE2 (...); + parameter DATA_RATE_OQ = "DDR"; + parameter DATA_RATE_TQ = "DDR"; + parameter integer DATA_WIDTH = 4; + parameter [0:0] INIT_OQ = 1'b0; + parameter [0:0] INIT_TQ = 1'b0; + parameter [0:0] IS_CLKDIV_INVERTED = 1'b0; + parameter [0:0] IS_CLK_INVERTED = 1'b0; + parameter [0:0] IS_D1_INVERTED = 1'b0; + parameter [0:0] IS_D2_INVERTED = 1'b0; + parameter [0:0] IS_D3_INVERTED = 1'b0; + parameter [0:0] IS_D4_INVERTED = 1'b0; + parameter [0:0] IS_D5_INVERTED = 1'b0; + parameter [0:0] IS_D6_INVERTED = 1'b0; + parameter [0:0] IS_D7_INVERTED = 1'b0; + parameter [0:0] IS_D8_INVERTED = 1'b0; + parameter [0:0] IS_T1_INVERTED = 1'b0; + parameter [0:0] IS_T2_INVERTED = 1'b0; + parameter [0:0] IS_T3_INVERTED = 1'b0; + parameter [0:0] IS_T4_INVERTED = 1'b0; + parameter SERDES_MODE = "MASTER"; + parameter [0:0] SRVAL_OQ = 1'b0; + parameter [0:0] SRVAL_TQ = 1'b0; + parameter TBYTE_CTL = "FALSE"; + parameter TBYTE_SRC = "FALSE"; + parameter integer TRISTATE_WIDTH = 4; + output OFB; + output OQ; + output SHIFTOUT1; + output SHIFTOUT2; + output TBYTEOUT; + output TFB; + output TQ; + input CLK; + input CLKDIV; + input D1; + input D2; + input D3; + input D4; + input D5; + input D6; + input D7; + input D8; + input OCE; + input RST; + input SHIFTIN1; + input SHIFTIN2; + input T1; + input T2; + input T3; + input T4; + input TBYTEIN; + input TCE; +endmodule + +module OUT_FIFO (...); + parameter integer ALMOST_EMPTY_VALUE = 1; + parameter integer ALMOST_FULL_VALUE = 1; + parameter ARRAY_MODE = "ARRAY_MODE_8_X_4"; + parameter OUTPUT_DISABLE = "FALSE"; + parameter SYNCHRONOUS_MODE = "FALSE"; + output ALMOSTEMPTY; + output ALMOSTFULL; + output EMPTY; + output FULL; + output [3:0] Q0; + output [3:0] Q1; + output [3:0] Q2; + output [3:0] Q3; + output [3:0] Q4; + output [3:0] Q7; + output [3:0] Q8; + output [3:0] Q9; + output [7:0] Q5; + output [7:0] Q6; + input RDCLK; + input RDEN; + input RESET; + input WRCLK; + input WREN; + input [7:0] D0; + input [7:0] D1; + input [7:0] D2; + input [7:0] D3; + input [7:0] D4; + input [7:0] D5; + input [7:0] D6; + input [7:0] D7; + input [7:0] D8; + input [7:0] D9; +endmodule + +module PHASER_IN (...); + parameter integer CLKOUT_DIV = 4; + parameter DQS_BIAS_MODE = "FALSE"; + parameter EN_ISERDES_RST = "FALSE"; + parameter integer FINE_DELAY = 0; + parameter FREQ_REF_DIV = "NONE"; + parameter [0:0] IS_RST_INVERTED = 1'b0; + parameter real MEMREFCLK_PERIOD = 0.000; + parameter OUTPUT_CLK_SRC = "PHASE_REF"; + parameter real PHASEREFCLK_PERIOD = 0.000; + parameter real REFCLK_PERIOD = 0.000; + parameter integer SEL_CLK_OFFSET = 5; + parameter SYNC_IN_DIV_RST = "FALSE"; + output FINEOVERFLOW; + output ICLK; + output ICLKDIV; + output ISERDESRST; + output RCLK; + output [5:0] COUNTERREADVAL; + input COUNTERLOADEN; + input COUNTERREADEN; + input DIVIDERST; + input EDGEADV; + input FINEENABLE; + input FINEINC; + input FREQREFCLK; + input MEMREFCLK; + input PHASEREFCLK; + input RST; + input SYNCIN; + input SYSCLK; + input [1:0] RANKSEL; + input [5:0] COUNTERLOADVAL; +endmodule + +module PHASER_IN_PHY (...); + parameter BURST_MODE = "FALSE"; + parameter integer CLKOUT_DIV = 4; + parameter [0:0] DQS_AUTO_RECAL = 1'b1; + parameter DQS_BIAS_MODE = "FALSE"; + parameter [2:0] DQS_FIND_PATTERN = 3'b001; + parameter integer FINE_DELAY = 0; + parameter FREQ_REF_DIV = "NONE"; + parameter [0:0] IS_RST_INVERTED = 1'b0; + parameter real MEMREFCLK_PERIOD = 0.000; + parameter OUTPUT_CLK_SRC = "PHASE_REF"; + parameter real PHASEREFCLK_PERIOD = 0.000; + parameter real REFCLK_PERIOD = 0.000; + parameter integer SEL_CLK_OFFSET = 5; + parameter SYNC_IN_DIV_RST = "FALSE"; + parameter WR_CYCLES = "FALSE"; + output DQSFOUND; + output DQSOUTOFRANGE; + output FINEOVERFLOW; + output ICLK; + output ICLKDIV; + output ISERDESRST; + output PHASELOCKED; + output RCLK; + output WRENABLE; + output [5:0] COUNTERREADVAL; + input BURSTPENDINGPHY; + input COUNTERLOADEN; + input COUNTERREADEN; + input FINEENABLE; + input FINEINC; + input FREQREFCLK; + input MEMREFCLK; + input PHASEREFCLK; + input RST; + input RSTDQSFIND; + input SYNCIN; + input SYSCLK; + input [1:0] ENCALIBPHY; + input [1:0] RANKSELPHY; + input [5:0] COUNTERLOADVAL; +endmodule + +module PHASER_OUT (...); + parameter integer CLKOUT_DIV = 4; + parameter COARSE_BYPASS = "FALSE"; + parameter integer COARSE_DELAY = 0; + parameter EN_OSERDES_RST = "FALSE"; + parameter integer FINE_DELAY = 0; + parameter [0:0] IS_RST_INVERTED = 1'b0; + parameter real MEMREFCLK_PERIOD = 0.000; + parameter OCLKDELAY_INV = "FALSE"; + parameter integer OCLK_DELAY = 0; + parameter OUTPUT_CLK_SRC = "PHASE_REF"; + parameter real PHASEREFCLK_PERIOD = 0.000; + parameter [2:0] PO = 3'b000; + parameter real REFCLK_PERIOD = 0.000; + parameter SYNC_IN_DIV_RST = "FALSE"; + output COARSEOVERFLOW; + output FINEOVERFLOW; + output OCLK; + output OCLKDELAYED; + output OCLKDIV; + output OSERDESRST; + output [8:0] COUNTERREADVAL; + input COARSEENABLE; + input COARSEINC; + input COUNTERLOADEN; + input COUNTERREADEN; + input DIVIDERST; + input EDGEADV; + input FINEENABLE; + input FINEINC; + input FREQREFCLK; + input MEMREFCLK; + input PHASEREFCLK; + input RST; + input SELFINEOCLKDELAY; + input SYNCIN; + input SYSCLK; + input [8:0] COUNTERLOADVAL; +endmodule + +module PHASER_OUT_PHY (...); + parameter integer CLKOUT_DIV = 4; + parameter COARSE_BYPASS = "FALSE"; + parameter integer COARSE_DELAY = 0; + parameter DATA_CTL_N = "FALSE"; + parameter DATA_RD_CYCLES = "FALSE"; + parameter integer FINE_DELAY = 0; + parameter [0:0] IS_RST_INVERTED = 1'b0; + parameter real MEMREFCLK_PERIOD = 0.000; + parameter OCLKDELAY_INV = "FALSE"; + parameter integer OCLK_DELAY = 0; + parameter OUTPUT_CLK_SRC = "PHASE_REF"; + parameter real PHASEREFCLK_PERIOD = 0.000; + parameter [2:0] PO = 3'b000; + parameter real REFCLK_PERIOD = 0.000; + parameter SYNC_IN_DIV_RST = "FALSE"; + output COARSEOVERFLOW; + output FINEOVERFLOW; + output OCLK; + output OCLKDELAYED; + output OCLKDIV; + output OSERDESRST; + output RDENABLE; + output [1:0] CTSBUS; + output [1:0] DQSBUS; + output [1:0] DTSBUS; + output [8:0] COUNTERREADVAL; + input BURSTPENDINGPHY; + input COARSEENABLE; + input COARSEINC; + input COUNTERLOADEN; + input COUNTERREADEN; + input FINEENABLE; + input FINEINC; + input FREQREFCLK; + input MEMREFCLK; + input PHASEREFCLK; + input RST; + input SELFINEOCLKDELAY; + input SYNCIN; + input SYSCLK; + input [1:0] ENCALIBPHY; + input [8:0] COUNTERLOADVAL; +endmodule + +module PHASER_REF (...); + parameter [0:0] IS_RST_INVERTED = 1'b0; + parameter [0:0] IS_PWRDWN_INVERTED = 1'b0; + output LOCKED; + input CLKIN; + input PWRDWN; + input RST; +endmodule + +module PHY_CONTROL (...); + parameter integer AO_TOGGLE = 0; + parameter [3:0] AO_WRLVL_EN = 4'b0000; + parameter BURST_MODE = "FALSE"; + parameter integer CLK_RATIO = 1; + parameter integer CMD_OFFSET = 0; + parameter integer CO_DURATION = 0; + parameter DATA_CTL_A_N = "FALSE"; + parameter DATA_CTL_B_N = "FALSE"; + parameter DATA_CTL_C_N = "FALSE"; + parameter DATA_CTL_D_N = "FALSE"; + parameter DISABLE_SEQ_MATCH = "TRUE"; + parameter integer DI_DURATION = 0; + parameter integer DO_DURATION = 0; + parameter integer EVENTS_DELAY = 63; + parameter integer FOUR_WINDOW_CLOCKS = 63; + parameter MULTI_REGION = "FALSE"; + parameter PHY_COUNT_ENABLE = "FALSE"; + parameter integer RD_CMD_OFFSET_0 = 0; + parameter integer RD_CMD_OFFSET_1 = 00; + parameter integer RD_CMD_OFFSET_2 = 0; + parameter integer RD_CMD_OFFSET_3 = 0; + parameter integer RD_DURATION_0 = 0; + parameter integer RD_DURATION_1 = 0; + parameter integer RD_DURATION_2 = 0; + parameter integer RD_DURATION_3 = 0; + parameter SYNC_MODE = "FALSE"; + parameter integer WR_CMD_OFFSET_0 = 0; + parameter integer WR_CMD_OFFSET_1 = 0; + parameter integer WR_CMD_OFFSET_2 = 0; + parameter integer WR_CMD_OFFSET_3 = 0; + parameter integer WR_DURATION_0 = 0; + parameter integer WR_DURATION_1 = 0; + parameter integer WR_DURATION_2 = 0; + parameter integer WR_DURATION_3 = 0; + output PHYCTLALMOSTFULL; + output PHYCTLEMPTY; + output PHYCTLFULL; + output PHYCTLREADY; + output [1:0] INRANKA; + output [1:0] INRANKB; + output [1:0] INRANKC; + output [1:0] INRANKD; + output [1:0] PCENABLECALIB; + output [3:0] AUXOUTPUT; + output [3:0] INBURSTPENDING; + output [3:0] OUTBURSTPENDING; + input MEMREFCLK; + input PHYCLK; + input PHYCTLMSTREMPTY; + input PHYCTLWRENABLE; + input PLLLOCK; + input READCALIBENABLE; + input REFDLLLOCK; + input RESET; + input SYNCIN; + input WRITECALIBENABLE; + input [31:0] PHYCTLWD; +endmodule + +module PLLE2_ADV (...); + parameter BANDWIDTH = "OPTIMIZED"; + parameter COMPENSATION = "ZHOLD"; + parameter STARTUP_WAIT = "FALSE"; + parameter integer CLKOUT0_DIVIDE = 1; + parameter integer CLKOUT1_DIVIDE = 1; + parameter integer CLKOUT2_DIVIDE = 1; + parameter integer CLKOUT3_DIVIDE = 1; + parameter integer CLKOUT4_DIVIDE = 1; + parameter integer CLKOUT5_DIVIDE = 1; + parameter integer DIVCLK_DIVIDE = 1; + parameter integer CLKFBOUT_MULT = 5; + parameter real CLKFBOUT_PHASE = 0.000; + parameter real CLKIN1_PERIOD = 0.000; + parameter real CLKIN2_PERIOD = 0.000; + parameter real CLKOUT0_DUTY_CYCLE = 0.500; + parameter real CLKOUT0_PHASE = 0.000; + parameter real CLKOUT1_DUTY_CYCLE = 0.500; + parameter real CLKOUT1_PHASE = 0.000; + parameter real CLKOUT2_DUTY_CYCLE = 0.500; + parameter real CLKOUT2_PHASE = 0.000; + parameter real CLKOUT3_DUTY_CYCLE = 0.500; + parameter real CLKOUT3_PHASE = 0.000; + parameter real CLKOUT4_DUTY_CYCLE = 0.500; + parameter real CLKOUT4_PHASE = 0.000; + parameter real CLKOUT5_DUTY_CYCLE = 0.500; + parameter real CLKOUT5_PHASE = 0.000; + parameter [0:0] IS_CLKINSEL_INVERTED = 1'b0; + parameter [0:0] IS_PWRDWN_INVERTED = 1'b0; + parameter [0:0] IS_RST_INVERTED = 1'b0; + parameter real REF_JITTER1 = 0.010; + parameter real REF_JITTER2 = 0.010; + parameter real VCOCLK_FREQ_MAX = 2133.000; + parameter real VCOCLK_FREQ_MIN = 800.000; + parameter real CLKIN_FREQ_MAX = 1066.000; + parameter real CLKIN_FREQ_MIN = 19.000; + parameter real CLKPFD_FREQ_MAX = 550.0; + parameter real CLKPFD_FREQ_MIN = 19.0; + output CLKFBOUT; + output CLKOUT0; + output CLKOUT1; + output CLKOUT2; + output CLKOUT3; + output CLKOUT4; + output CLKOUT5; + output DRDY; + output LOCKED; + output [15:0] DO; + input CLKFBIN; + input CLKIN1; + input CLKIN2; + input CLKINSEL; + input DCLK; + input DEN; + input DWE; + input PWRDWN; + input RST; + input [15:0] DI; + input [6:0] DADDR; +endmodule + +module PLLE2_BASE (...); + parameter BANDWIDTH = "OPTIMIZED"; + parameter integer CLKFBOUT_MULT = 5; + parameter real CLKFBOUT_PHASE = 0.000; + parameter real CLKIN1_PERIOD = 0.000; + parameter integer CLKOUT0_DIVIDE = 1; + parameter real CLKOUT0_DUTY_CYCLE = 0.500; + parameter real CLKOUT0_PHASE = 0.000; + parameter integer CLKOUT1_DIVIDE = 1; + parameter real CLKOUT1_DUTY_CYCLE = 0.500; + parameter real CLKOUT1_PHASE = 0.000; + parameter integer CLKOUT2_DIVIDE = 1; + parameter real CLKOUT2_DUTY_CYCLE = 0.500; + parameter real CLKOUT2_PHASE = 0.000; + parameter integer CLKOUT3_DIVIDE = 1; + parameter real CLKOUT3_DUTY_CYCLE = 0.500; + parameter real CLKOUT3_PHASE = 0.000; + parameter integer CLKOUT4_DIVIDE = 1; + parameter real CLKOUT4_DUTY_CYCLE = 0.500; + parameter real CLKOUT4_PHASE = 0.000; + parameter integer CLKOUT5_DIVIDE = 1; + parameter real CLKOUT5_DUTY_CYCLE = 0.500; + parameter real CLKOUT5_PHASE = 0.000; + parameter integer DIVCLK_DIVIDE = 1; + parameter real REF_JITTER1 = 0.010; + parameter STARTUP_WAIT = "FALSE"; + output CLKFBOUT; + output CLKOUT0; + output CLKOUT1; + output CLKOUT2; + output CLKOUT3; + output CLKOUT4; + output CLKOUT5; + output LOCKED; + input CLKFBIN; + input CLKIN1; + input PWRDWN; + input RST; +endmodule + +module PULLDOWN (...); + output O; +endmodule + +module PULLUP (...); + output O; +endmodule + +module RAM128X1S (...); + parameter [127:0] INIT = 128'h00000000000000000000000000000000; + parameter [0:0] IS_WCLK_INVERTED = 1'b0; + output O; + input A0, A1, A2, A3, A4, A5, A6, D, WCLK, WE; +endmodule + +module RAM256X1S (...); + parameter [255:0] INIT = 256'h0; + parameter [0:0] IS_WCLK_INVERTED = 1'b0; + output O; + input [7:0] A; + input D; + input WCLK; + input WE; +endmodule + +module RAM32M (...); + parameter [63:0] INIT_A = 64'h0000000000000000; + parameter [63:0] INIT_B = 64'h0000000000000000; + parameter [63:0] INIT_C = 64'h0000000000000000; + parameter [63:0] INIT_D = 64'h0000000000000000; + parameter [0:0] IS_WCLK_INVERTED = 1'b0; + output [1:0] DOA; + output [1:0] DOB; + output [1:0] DOC; + output [1:0] DOD; + input [4:0] ADDRA; + input [4:0] ADDRB; + input [4:0] ADDRC; + input [4:0] ADDRD; + input [1:0] DIA; + input [1:0] DIB; + input [1:0] DIC; + input [1:0] DID; + input WCLK; + input WE; +endmodule + +module RAM32X1D (...); + parameter [31:0] INIT = 32'h00000000; + parameter [0:0] IS_WCLK_INVERTED = 1'b0; + output DPO, SPO; + input A0, A1, A2, A3, A4, D, DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, WCLK, WE; +endmodule + +module RAM32X1S (...); + parameter [31:0] INIT = 32'h00000000; + parameter [0:0] IS_WCLK_INVERTED = 1'b0; + output O; + input A0, A1, A2, A3, A4, D, WCLK, WE; +endmodule + +module RAM32X1S_1 (...); + parameter [31:0] INIT = 32'h00000000; + parameter [0:0] IS_WCLK_INVERTED = 1'b0; + output O; + input A0, A1, A2, A3, A4, D, WCLK, WE; +endmodule + +module RAM32X2S (...); + parameter [31:0] INIT_00 = 32'h00000000; + parameter [31:0] INIT_01 = 32'h00000000; + parameter [0:0] IS_WCLK_INVERTED = 1'b0; + output O0, O1; + input A0, A1, A2, A3, A4, D0, D1, WCLK, WE; +endmodule + +module RAM64M (...); + parameter [63:0] INIT_A = 64'h0000000000000000; + parameter [63:0] INIT_B = 64'h0000000000000000; + parameter [63:0] INIT_C = 64'h0000000000000000; + parameter [63:0] INIT_D = 64'h0000000000000000; + parameter [0:0] IS_WCLK_INVERTED = 1'b0; + output DOA; + output DOB; + output DOC; + output DOD; + input [5:0] ADDRA; + input [5:0] ADDRB; + input [5:0] ADDRC; + input [5:0] ADDRD; + input DIA; + input DIB; + input DIC; + input DID; + input WCLK; + input WE; +endmodule + +module RAM64X1S (...); + parameter [63:0] INIT = 64'h0000000000000000; + parameter [0:0] IS_WCLK_INVERTED = 1'b0; + output O; + input A0, A1, A2, A3, A4, A5, D, WCLK, WE; +endmodule + +module RAM64X1S_1 (...); + parameter [63:0] INIT = 64'h0000000000000000; + parameter [0:0] IS_WCLK_INVERTED = 1'b0; + output O; + input A0, A1, A2, A3, A4, A5, D, WCLK, WE; +endmodule + +module RAM64X2S (...); + parameter [63:0] INIT_00 = 64'h0000000000000000; + parameter [63:0] INIT_01 = 64'h0000000000000000; + parameter [0:0] IS_WCLK_INVERTED = 1'b0; + output O0, O1; + input A0, A1, A2, A3, A4, A5, D0, D1, WCLK, WE; +endmodule + +module ROM128X1 (...); + parameter [127:0] INIT = 128'h00000000000000000000000000000000; + output O; + input A0, A1, A2, A3, A4, A5, A6; +endmodule + +module ROM256X1 (...); + parameter [255:0] INIT = 256'h0000000000000000000000000000000000000000000000000000000000000000; + output O; + input A0, A1, A2, A3, A4, A5, A6, A7; +endmodule + +module ROM32X1 (...); + parameter [31:0] INIT = 32'h00000000; + output O; + input A0, A1, A2, A3, A4; +endmodule + +module ROM64X1 (...); + parameter [63:0] INIT = 64'h0000000000000000; + output O; + input A0, A1, A2, A3, A4, A5; +endmodule + +module SRL16E (...); + parameter [15:0] INIT = 16'h0000; + parameter [0:0] IS_CLK_INVERTED = 1'b0; + output Q; + input A0, A1, A2, A3, CE, CLK, D; +endmodule + +module SRLC32E (...); + parameter [31:0] INIT = 32'h00000000; + parameter [0:0] IS_CLK_INVERTED = 1'b0; + output Q; + output Q31; + input [4:0] A; + input CE, CLK, D; +endmodule + +module STARTUPE2 (...); + parameter PROG_USR = "FALSE"; + parameter real SIM_CCLK_FREQ = 0.0; + output CFGCLK; + output CFGMCLK; + output EOS; + output PREQ; + input CLK; + input GSR; + input GTS; + input KEYCLEARB; + input PACK; + input USRCCLKO; + input USRCCLKTS; + input USRDONEO; + input USRDONETS; +endmodule + +module USR_ACCESSE2 (...); + output CFGCLK; + output DATAVALID; + output [31:0] DATA; +endmodule + +module XADC (...); + output BUSY; + output DRDY; + output EOC; + output EOS; + output JTAGBUSY; + output JTAGLOCKED; + output JTAGMODIFIED; + output OT; + output [15:0] DO; + output [7:0] ALM; + output [4:0] CHANNEL; + output [4:0] MUXADDR; + input CONVST; + input CONVSTCLK; + input DCLK; + input DEN; + input DWE; + input RESET; + input VN; + input VP; + input [15:0] DI; + input [15:0] VAUXN; + input [15:0] VAUXP; + input [6:0] DADDR; + parameter [15:0] INIT_40 = 16'h0; + parameter [15:0] INIT_41 = 16'h0; + parameter [15:0] INIT_42 = 16'h0800; + parameter [15:0] INIT_43 = 16'h0; + parameter [15:0] INIT_44 = 16'h0; + parameter [15:0] INIT_45 = 16'h0; + parameter [15:0] INIT_46 = 16'h0; + parameter [15:0] INIT_47 = 16'h0; + parameter [15:0] INIT_48 = 16'h0; + parameter [15:0] INIT_49 = 16'h0; + parameter [15:0] INIT_4A = 16'h0; + parameter [15:0] INIT_4B = 16'h0; + parameter [15:0] INIT_4C = 16'h0; + parameter [15:0] INIT_4D = 16'h0; + parameter [15:0] INIT_4E = 16'h0; + parameter [15:0] INIT_4F = 16'h0; + parameter [15:0] INIT_50 = 16'h0; + parameter [15:0] INIT_51 = 16'h0; + parameter [15:0] INIT_52 = 16'h0; + parameter [15:0] INIT_53 = 16'h0; + parameter [15:0] INIT_54 = 16'h0; + parameter [15:0] INIT_55 = 16'h0; + parameter [15:0] INIT_56 = 16'h0; + parameter [15:0] INIT_57 = 16'h0; + parameter [15:0] INIT_58 = 16'h0; + parameter [15:0] INIT_59 = 16'h0; + parameter [15:0] INIT_5A = 16'h0; + parameter [15:0] INIT_5B = 16'h0; + parameter [15:0] INIT_5C = 16'h0; + parameter [15:0] INIT_5D = 16'h0; + parameter [15:0] INIT_5E = 16'h0; + parameter [15:0] INIT_5F = 16'h0; + parameter IS_CONVSTCLK_INVERTED = 1'b0; + parameter IS_DCLK_INVERTED = 1'b0; + parameter SIM_DEVICE = "7SERIES"; + parameter SIM_MONITOR_FILE = "design.txt"; +endmodule + diff --git a/techlibs/xilinx/drams.txt b/techlibs/xilinx/drams.txt new file mode 100644 index 000000000..e6635d0e2 --- /dev/null +++ b/techlibs/xilinx/drams.txt @@ -0,0 +1,36 @@ + +bram $__XILINX_RAM64X1D + init 1 + abits 6 + dbits 1 + groups 2 + ports 1 1 + wrmode 0 1 + enable 0 1 + transp 0 0 + clocks 0 1 + clkpol 0 2 +endbram + +bram $__XILINX_RAM128X1D + init 1 + abits 7 + dbits 1 + groups 2 + ports 1 1 + wrmode 0 1 + enable 0 1 + transp 0 0 + clocks 0 1 + clkpol 0 2 +endbram + +match $__XILINX_RAM64X1D + make_outreg + or_next_if_better +endmatch + +match $__XILINX_RAM128X1D + make_outreg +endmatch + diff --git a/techlibs/xilinx/drams_bb.v b/techlibs/xilinx/drams_bb.v new file mode 100644 index 000000000..11168fe13 --- /dev/null +++ b/techlibs/xilinx/drams_bb.v @@ -0,0 +1,20 @@ + +module RAM64X1D ( + output DPO, SPO, + input D, WCLK, WE, + input A0, A1, A2, A3, A4, A5, + input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5 +); + parameter INIT = 64'h0; + parameter IS_WCLK_INVERTED = 1'b0; +endmodule + +module RAM128X1D ( + output DPO, SPO, + input D, WCLK, WE, + input [6:0] A, DPRA +); + parameter INIT = 128'h0; + parameter IS_WCLK_INVERTED = 1'b0; +endmodule + diff --git a/techlibs/xilinx/drams_map.v b/techlibs/xilinx/drams_map.v new file mode 100644 index 000000000..47476b592 --- /dev/null +++ b/techlibs/xilinx/drams_map.v @@ -0,0 +1,63 @@ + +module \$__XILINX_RAM64X1D (CLK1, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); + parameter [63:0] INIT = 64'bx; + parameter CLKPOL2 = 1; + input CLK1; + + input [5:0] A1ADDR; + output A1DATA; + + input [5:0] B1ADDR; + input B1DATA; + input B1EN; + + RAM64X1D #( + .INIT(INIT), + .IS_WCLK_INVERTED(!CLKPOL2) + ) _TECHMAP_REPLACE_ ( + .DPRA0(A1ADDR[0]), + .DPRA1(A1ADDR[1]), + .DPRA2(A1ADDR[2]), + .DPRA3(A1ADDR[3]), + .DPRA4(A1ADDR[4]), + .DPRA5(A1ADDR[5]), + .DPO(A1DATA), + + .A0(B1ADDR[0]), + .A1(B1ADDR[1]), + .A2(B1ADDR[2]), + .A3(B1ADDR[3]), + .A4(B1ADDR[4]), + .A5(B1ADDR[5]), + .D(B1DATA), + .WCLK(CLK1), + .WE(B1EN) + ); +endmodule + +module \$__XILINX_RAM128X1D (CLK1, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); + parameter [127:0] INIT = 128'bx; + parameter CLKPOL2 = 1; + input CLK1; + + input [6:0] A1ADDR; + output A1DATA; + + input [6:0] B1ADDR; + input B1DATA; + input B1EN; + + RAM128X1D #( + .INIT(INIT), + .IS_WCLK_INVERTED(!CLKPOL2) + ) _TECHMAP_REPLACE_ ( + .DPRA(A1ADDR), + .DPO(A1DATA), + + .A(B1ADDR), + .D(B1DATA), + .WCLK(CLK1), + .WE(B1EN) + ); +endmodule + diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index 7812fa290..e7ec1e6e8 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -2,11 +2,11 @@ * 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 @@ -47,7 +47,7 @@ struct SynthXilinxPass : public Pass { log("compatible with 7-Series Xilinx devices.\n"); log("\n"); log(" -top <module>\n"); - log(" use the specified module as top module (default='top')\n"); + log(" use the specified module as top module\n"); log("\n"); log(" -edif <file>\n"); log(" write the design to the specified edif file. writing of an output file\n"); @@ -69,6 +69,9 @@ struct SynthXilinxPass : public Pass { log("\n"); log(" begin:\n"); log(" read_verilog -lib +/xilinx/cells_sim.v\n"); + log(" read_verilog -lib +/xilinx/cells_xtra.v\n"); + log(" read_verilog -lib +/xilinx/brams_bb.v\n"); + log(" read_verilog -lib +/xilinx/drams_bb.v\n"); log(" hierarchy -check -top <top>\n"); log("\n"); log(" flatten: (only if -flatten)\n"); @@ -77,35 +80,45 @@ struct SynthXilinxPass : public Pass { log("\n"); log(" coarse:\n"); log(" synth -run coarse\n"); - log(" dff2dffe\n"); log("\n"); log(" bram:\n"); log(" memory_bram -rules +/xilinx/brams.txt\n"); log(" techmap -map +/xilinx/brams_map.v\n"); log("\n"); + log(" dram:\n"); + log(" memory_bram -rules +/xilinx/drams.txt\n"); + log(" techmap -map +/xilinx/drams_map.v\n"); + log("\n"); log(" fine:\n"); log(" opt -fast -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"); log(" techmap -map +/xilinx/cells_map.v\n"); + log(" dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT\n"); log(" clean\n"); log("\n"); - log(" edif:\n"); - log(" write_edif synth.edif\n"); + log(" check:\n"); + log(" hierarchy -check\n"); + log(" stat\n"); + log(" check -noinit\n"); + log("\n"); + log(" edif: (only if -edif)\n"); + log(" write_edif <file-name>\n"); log("\n"); } virtual void execute(std::vector<std::string> args, RTLIL::Design *design) { - std::string top_module = "top"; - std::string arch_name = "spartan6"; + std::string top_opt = "-auto-top"; std::string edif_file; std::string run_from, run_to; bool flatten = false; @@ -115,7 +128,7 @@ struct SynthXilinxPass : public Pass { for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-top" && argidx+1 < args.size()) { - top_module = args[++argidx]; + top_opt = "-top " + args[++argidx]; continue; } if (args[argidx] == "-edif" && argidx+1 < args.size()) { @@ -147,13 +160,16 @@ struct SynthXilinxPass : public Pass { bool active = run_from.empty(); - log_header("Executing SYNTH_XILINX pass.\n"); + log_header(design, "Executing SYNTH_XILINX pass.\n"); log_push(); if (check_label(active, run_from, run_to, "begin")) { Pass::call(design, "read_verilog -lib +/xilinx/cells_sim.v"); - Pass::call(design, stringf("hierarchy -check -top %s", top_module.c_str())); + Pass::call(design, "read_verilog -lib +/xilinx/cells_xtra.v"); + Pass::call(design, "read_verilog -lib +/xilinx/brams_bb.v"); + Pass::call(design, "read_verilog -lib +/xilinx/drams_bb.v"); + Pass::call(design, stringf("hierarchy -check %s", top_opt.c_str())); } if (flatten && check_label(active, run_from, run_to, "flatten")) @@ -165,7 +181,6 @@ struct SynthXilinxPass : public Pass { if (check_label(active, run_from, run_to, "coarse")) { Pass::call(design, "synth -run coarse"); - Pass::call(design, "dff2dffe"); } if (check_label(active, run_from, run_to, "bram")) @@ -174,10 +189,18 @@ struct SynthXilinxPass : public Pass { Pass::call(design, "techmap -map +/xilinx/brams_map.v"); } + if (check_label(active, run_from, run_to, "dram")) + { + Pass::call(design, "memory_bram -rules +/xilinx/drams.txt"); + Pass::call(design, "techmap -map +/xilinx/drams_map.v"); + } + if (check_label(active, run_from, run_to, "fine")) { 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"); Pass::call(design, "opt -fast"); @@ -185,16 +208,24 @@ 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"); } if (check_label(active, run_from, run_to, "map_cells")) { Pass::call(design, "techmap -map +/xilinx/cells_map.v"); + Pass::call(design, "dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT"); Pass::call(design, "clean"); } + if (check_label(active, run_from, run_to, "check")) + { + Pass::call(design, "hierarchy -check"); + Pass::call(design, "stat"); + Pass::call(design, "check -noinit"); + } + if (check_label(active, run_from, run_to, "edif")) { if (!edif_file.empty()) @@ -204,5 +235,5 @@ struct SynthXilinxPass : public Pass { log_pop(); } } SynthXilinxPass; - + PRIVATE_NAMESPACE_END diff --git a/techlibs/xilinx/tests/.gitignore b/techlibs/xilinx/tests/.gitignore index bc2f8babf..496b87461 100644 --- a/techlibs/xilinx/tests/.gitignore +++ b/techlibs/xilinx/tests/.gitignore @@ -1,3 +1,6 @@ bram1_cmp bram1.mk bram1_[0-9]*/ +bram2.log +bram2_syn.v +bram2_tb diff --git a/techlibs/xilinx/tests/bram1.sh b/techlibs/xilinx/tests/bram1.sh index 1f0359ac9..7451a1b3e 100644 --- a/techlibs/xilinx/tests/bram1.sh +++ b/techlibs/xilinx/tests/bram1.sh @@ -56,7 +56,8 @@ echo "Testing..." ${MAKE:-make} -f bram1.mk echo -echo "Used rules:" $(grep -h 'Selected rule.*with efficiency' bram1_*/synth.log | gawk '{ print $3; }' | sort -u) +echo "Used bram types:" +grep -h 'Mapping to bram type' bram1_*/synth.log | sort | uniq -c echo "Cleaning up..." rm -rf bram1_cmp bram1.mk bram1_[0-9]*/ diff --git a/techlibs/xilinx/tests/bram1.v b/techlibs/xilinx/tests/bram1.v index 034cc18e9..ac7140a04 100644 --- a/techlibs/xilinx/tests/bram1.v +++ b/techlibs/xilinx/tests/bram1.v @@ -10,10 +10,27 @@ module bram1 #( input [ABITS-1:0] RD_ADDR, output [DBITS-1:0] RD_DATA ); + localparam [ABITS-1:0] INIT_ADDR_0 = 1234; + localparam [ABITS-1:0] INIT_ADDR_1 = 4321; + localparam [ABITS-1:0] INIT_ADDR_2 = 2**ABITS-1; + localparam [ABITS-1:0] INIT_ADDR_3 = (2**ABITS-1) / 2; + + localparam [DBITS-1:0] INIT_DATA_0 = 128'h 51e152a7300e309ccb8cd06d34558f49; + localparam [DBITS-1:0] INIT_DATA_1 = 128'h 07b1fe94a530ddf3027520f9d23ab43e; + localparam [DBITS-1:0] INIT_DATA_2 = 128'h 3cedc6de43ef3f607af3193658d0eb0b; + localparam [DBITS-1:0] INIT_DATA_3 = 128'h f6bc5514a8abf1e2810df966bcc13b46; + reg [DBITS-1:0] memory [0:2**ABITS-1]; reg [ABITS-1:0] RD_ADDR_BUF; reg [DBITS-1:0] RD_DATA_BUF; + initial begin + memory[INIT_ADDR_0] <= INIT_DATA_0; + memory[INIT_ADDR_1] <= INIT_DATA_1; + memory[INIT_ADDR_2] <= INIT_DATA_2; + memory[INIT_ADDR_3] <= INIT_DATA_3; + end + always @(posedge clk) begin if (WR_EN) memory[WR_ADDR] <= WR_DATA; RD_ADDR_BUF <= RD_ADDR; diff --git a/techlibs/xilinx/tests/bram1_tb.v b/techlibs/xilinx/tests/bram1_tb.v index 8f854b749..e75dfe31d 100644 --- a/techlibs/xilinx/tests/bram1_tb.v +++ b/techlibs/xilinx/tests/bram1_tb.v @@ -8,6 +8,16 @@ module bram1_tb #( reg [ABITS-1:0] RD_ADDR; wire [DBITS-1:0] RD_DATA; + localparam [ABITS-1:0] INIT_ADDR_0 = 1234; + localparam [ABITS-1:0] INIT_ADDR_1 = 4321; + localparam [ABITS-1:0] INIT_ADDR_2 = 2**ABITS-1; + localparam [ABITS-1:0] INIT_ADDR_3 = (2**ABITS-1) / 2; + + localparam [DBITS-1:0] INIT_DATA_0 = 128'h 51e152a7300e309ccb8cd06d34558f49; + localparam [DBITS-1:0] INIT_DATA_1 = 128'h 07b1fe94a530ddf3027520f9d23ab43e; + localparam [DBITS-1:0] INIT_DATA_2 = 128'h 3cedc6de43ef3f607af3193658d0eb0b; + localparam [DBITS-1:0] INIT_DATA_3 = 128'h f6bc5514a8abf1e2810df966bcc13b46; + bram1 #( // .ABITS(ABITS), // .DBITS(DBITS), @@ -68,6 +78,11 @@ module bram1_tb #( // $dumpfile("testbench.vcd"); // $dumpvars(0, bram1_tb); + memory[INIT_ADDR_0] = INIT_DATA_0; + memory[INIT_ADDR_1] = INIT_DATA_1; + memory[INIT_ADDR_2] = INIT_DATA_2; + memory[INIT_ADDR_3] = INIT_DATA_3; + xorshift64_next; xorshift64_next; xorshift64_next; @@ -84,16 +99,33 @@ module bram1_tb #( clk <= 0; for (i = 0; i < 512; i = i+1) begin - if (DBITS > 64) - WR_DATA <= (xorshift64_state << (DBITS-64)) ^ xorshift64_state; - else - WR_DATA <= xorshift64_state; - xorshift64_next; - WR_ADDR <= getaddr(i < 256 ? i[7:4] : xorshift64_state[63:60]); - xorshift64_next; - RD_ADDR <= getaddr(i < 256 ? i[3:0] : xorshift64_state[59:56]); - WR_EN <= xorshift64_state[55]; - xorshift64_next; + if (i == 0) begin + WR_EN <= 0; + RD_ADDR <= INIT_ADDR_0; + end else + if (i == 1) begin + WR_EN <= 0; + RD_ADDR <= INIT_ADDR_1; + end else + if (i == 2) begin + WR_EN <= 0; + RD_ADDR <= INIT_ADDR_2; + end else + if (i == 3) begin + WR_EN <= 0; + RD_ADDR <= INIT_ADDR_3; + end else begin + if (DBITS > 64) + WR_DATA <= (xorshift64_state << (DBITS-64)) ^ xorshift64_state; + else + WR_DATA <= xorshift64_state; + xorshift64_next; + WR_ADDR <= getaddr(i < 256 ? i[7:4] : xorshift64_state[63:60]); + xorshift64_next; + RD_ADDR <= getaddr(i < 256 ? i[3:0] : xorshift64_state[59:56]); + WR_EN <= xorshift64_state[55]; + xorshift64_next; + end #1; clk <= 1; #1; clk <= 0; diff --git a/techlibs/xilinx/tests/bram2.sh b/techlibs/xilinx/tests/bram2.sh new file mode 100644 index 000000000..5d9c84dac --- /dev/null +++ b/techlibs/xilinx/tests/bram2.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -ex +unisims=/opt/Xilinx/Vivado/2014.4/data/verilog/src/unisims +../../../yosys -v2 -l bram2.log -p synth_xilinx -o bram2_syn.v bram2.v +iverilog -T typ -o bram2_tb bram2_tb.v bram2_syn.v -y $unisims $unisims/../glbl.v +vvp -N bram2_tb + diff --git a/techlibs/xilinx/tests/bram2.v b/techlibs/xilinx/tests/bram2.v new file mode 100644 index 000000000..0a6013ca6 --- /dev/null +++ b/techlibs/xilinx/tests/bram2.v @@ -0,0 +1,35 @@ +module myram( + input rd_clk, + input [ 7:0] rd_addr, + output reg [17:0] rd_data, + input wr_clk, + input wr_enable, + input [ 7:0] wr_addr, + input [17:0] wr_data +); + reg [17:0] memory [0:255]; + integer i; + + function [17:0] hash(input [7:0] k); + reg [31:0] x; + begin + x = {k, ~k, k, ~k}; + x = x ^ (x << 13); + x = x ^ (x >> 17); + x = x ^ (x << 5); + hash = x; + end + endfunction + + initial begin + for (i = 0; i < 256; i = i+1) + memory[i] = hash(i); + end + + always @(posedge rd_clk) + rd_data <= memory[rd_addr]; + + always @(posedge wr_clk) + if (wr_enable) + memory[wr_addr] <= wr_data; +endmodule diff --git a/techlibs/xilinx/tests/bram2_tb.v b/techlibs/xilinx/tests/bram2_tb.v new file mode 100644 index 000000000..0fe4137c6 --- /dev/null +++ b/techlibs/xilinx/tests/bram2_tb.v @@ -0,0 +1,56 @@ +`timescale 1 ns / 1 ps + +module testbench; + reg rd_clk; + reg [ 7:0] rd_addr; + wire [17:0] rd_data; + + wire wr_clk = 0; + wire wr_enable = 0; + wire [ 7:0] wr_addr = 0; + wire [17:0] wr_data = 0; + + function [17:0] hash(input [7:0] k); + reg [31:0] x; + begin + x = {k, ~k, k, ~k}; + x = x ^ (x << 13); + x = x ^ (x >> 17); + x = x ^ (x << 5); + hash = x; + end + endfunction + + myram uut ( + .rd_clk (rd_clk ), + .rd_addr (rd_addr ), + .rd_data (rd_data ), + .wr_clk (wr_clk ), + .wr_enable(wr_enable), + .wr_addr (wr_addr ), + .wr_data (wr_data ) + ); + + initial begin + rd_clk = 0; + #1000; + forever #10 rd_clk <= ~rd_clk; + end + + integer i; + initial begin + rd_addr <= 0; + @(posedge rd_clk); + for (i = 0; i < 256; i=i+1) begin + rd_addr <= rd_addr + 1; + @(posedge rd_clk); + // $display("%3d %3d", i, rd_data); + if (hash(i) !== rd_data) begin + $display("[%1t] ERROR: addr=%3d, data_mem=%18b, data_ref=%18b", $time, i, rd_data, hash(i)); + $stop; + end + end + $display("[%1t] Passed bram2 test.", $time); + $finish; + end +endmodule diff --git a/tests/asicworld/README b/tests/asicworld/README index 0e96edb7b..4657e7a27 100644 --- a/tests/asicworld/README +++ b/tests/asicworld/README @@ -1 +1 @@ -Borrowed verilog examples from http://www.asic-world.com/. +Borrowed Verilog examples from http://www.asic-world.com/. diff --git a/tests/asicworld/code_hdl_models_arbiter.v b/tests/asicworld/code_hdl_models_arbiter.v index 978e1987b..d3e3a66f1 100644 --- a/tests/asicworld/code_hdl_models_arbiter.v +++ b/tests/asicworld/code_hdl_models_arbiter.v @@ -1,123 +1,123 @@ -//----------------------------------------------------
-// A four level, round-robin arbiter. This was
-// orginally coded by WD Peterson in VHDL.
-//----------------------------------------------------
-module arbiter (
- clk,
- rst,
- req3,
- req2,
- req1,
- req0,
- gnt3,
- gnt2,
- gnt1,
- gnt0
-);
-// --------------Port Declaration-----------------------
-input clk;
-input rst;
-input req3;
-input req2;
-input req1;
-input req0;
-output gnt3;
-output gnt2;
-output gnt1;
-output gnt0;
-
-//--------------Internal Registers----------------------
-wire [1:0] gnt ;
-wire comreq ;
-wire beg ;
-wire [1:0] lgnt ;
-wire lcomreq ;
-reg lgnt0 ;
-reg lgnt1 ;
-reg lgnt2 ;
-reg lgnt3 ;
-reg lasmask ;
-reg lmask0 ;
-reg lmask1 ;
-reg ledge ;
-
-//--------------Code Starts Here-----------------------
-always @ (posedge clk)
-if (rst) begin
- lgnt0 <= 0;
- lgnt1 <= 0;
- lgnt2 <= 0;
- lgnt3 <= 0;
-end else begin
- lgnt0 <=(~lcomreq & ~lmask1 & ~lmask0 & ~req3 & ~req2 & ~req1 & req0)
- | (~lcomreq & ~lmask1 & lmask0 & ~req3 & ~req2 & req0)
- | (~lcomreq & lmask1 & ~lmask0 & ~req3 & req0)
- | (~lcomreq & lmask1 & lmask0 & req0 )
- | ( lcomreq & lgnt0 );
- lgnt1 <=(~lcomreq & ~lmask1 & ~lmask0 & req1)
- | (~lcomreq & ~lmask1 & lmask0 & ~req3 & ~req2 & req1 & ~req0)
- | (~lcomreq & lmask1 & ~lmask0 & ~req3 & req1 & ~req0)
- | (~lcomreq & lmask1 & lmask0 & req1 & ~req0)
- | ( lcomreq & lgnt1);
- lgnt2 <=(~lcomreq & ~lmask1 & ~lmask0 & req2 & ~req1)
- | (~lcomreq & ~lmask1 & lmask0 & req2)
- | (~lcomreq & lmask1 & ~lmask0 & ~req3 & req2 & ~req1 & ~req0)
- | (~lcomreq & lmask1 & lmask0 & req2 & ~req1 & ~req0)
- | ( lcomreq & lgnt2);
- lgnt3 <=(~lcomreq & ~lmask1 & ~lmask0 & req3 & ~req2 & ~req1)
- | (~lcomreq & ~lmask1 & lmask0 & req3 & ~req2)
- | (~lcomreq & lmask1 & ~lmask0 & req3)
- | (~lcomreq & lmask1 & lmask0 & req3 & ~req2 & ~req1 & ~req0)
- | ( lcomreq & lgnt3);
-end
-
-//----------------------------------------------------
-// lasmask state machine.
-//----------------------------------------------------
-assign beg = (req3 | req2 | req1 | req0) & ~lcomreq;
-always @ (posedge clk)
-begin
- lasmask <= (beg & ~ledge & ~lasmask);
- ledge <= (beg & ~ledge & lasmask)
- | (beg & ledge & ~lasmask);
-end
-
-//----------------------------------------------------
-// comreq logic.
-//----------------------------------------------------
-assign lcomreq = ( req3 & lgnt3 )
- | ( req2 & lgnt2 )
- | ( req1 & lgnt1 )
- | ( req0 & lgnt0 );
-
-//----------------------------------------------------
-// Encoder logic.
-//----------------------------------------------------
-assign lgnt = {(lgnt3 | lgnt2),(lgnt3 | lgnt1)};
-
-//----------------------------------------------------
-// lmask register.
-//----------------------------------------------------
-always @ (posedge clk )
-if( rst ) begin
- lmask1 <= 0;
- lmask0 <= 0;
-end else if(lasmask) begin
- lmask1 <= lgnt[1];
- lmask0 <= lgnt[0];
-end else begin
- lmask1 <= lmask1;
- lmask0 <= lmask0;
-end
-
-assign comreq = lcomreq;
-assign gnt = lgnt;
-//----------------------------------------------------
-// Drive the outputs
-//----------------------------------------------------
-assign gnt3 = lgnt3;
-assign gnt2 = lgnt2;
-assign gnt1 = lgnt1;
-assign gnt0 = lgnt0;
-
-endmodule
+//---------------------------------------------------- +// A four level, round-robin arbiter. This was +// orginally coded by WD Peterson in VHDL. +//---------------------------------------------------- +module arbiter ( + clk, + rst, + req3, + req2, + req1, + req0, + gnt3, + gnt2, + gnt1, + gnt0 +); +// --------------Port Declaration----------------------- +input clk; +input rst; +input req3; +input req2; +input req1; +input req0; +output gnt3; +output gnt2; +output gnt1; +output gnt0; + +//--------------Internal Registers---------------------- +wire [1:0] gnt ; +wire comreq ; +wire beg ; +wire [1:0] lgnt ; +wire lcomreq ; +reg lgnt0 ; +reg lgnt1 ; +reg lgnt2 ; +reg lgnt3 ; +reg lasmask ; +reg lmask0 ; +reg lmask1 ; +reg ledge ; + +//--------------Code Starts Here----------------------- +always @ (posedge clk) +if (rst) begin + lgnt0 <= 0; + lgnt1 <= 0; + lgnt2 <= 0; + lgnt3 <= 0; +end else begin + lgnt0 <=(~lcomreq & ~lmask1 & ~lmask0 & ~req3 & ~req2 & ~req1 & req0) + | (~lcomreq & ~lmask1 & lmask0 & ~req3 & ~req2 & req0) + | (~lcomreq & lmask1 & ~lmask0 & ~req3 & req0) + | (~lcomreq & lmask1 & lmask0 & req0 ) + | ( lcomreq & lgnt0 ); + lgnt1 <=(~lcomreq & ~lmask1 & ~lmask0 & req1) + | (~lcomreq & ~lmask1 & lmask0 & ~req3 & ~req2 & req1 & ~req0) + | (~lcomreq & lmask1 & ~lmask0 & ~req3 & req1 & ~req0) + | (~lcomreq & lmask1 & lmask0 & req1 & ~req0) + | ( lcomreq & lgnt1); + lgnt2 <=(~lcomreq & ~lmask1 & ~lmask0 & req2 & ~req1) + | (~lcomreq & ~lmask1 & lmask0 & req2) + | (~lcomreq & lmask1 & ~lmask0 & ~req3 & req2 & ~req1 & ~req0) + | (~lcomreq & lmask1 & lmask0 & req2 & ~req1 & ~req0) + | ( lcomreq & lgnt2); + lgnt3 <=(~lcomreq & ~lmask1 & ~lmask0 & req3 & ~req2 & ~req1) + | (~lcomreq & ~lmask1 & lmask0 & req3 & ~req2) + | (~lcomreq & lmask1 & ~lmask0 & req3) + | (~lcomreq & lmask1 & lmask0 & req3 & ~req2 & ~req1 & ~req0) + | ( lcomreq & lgnt3); +end + +//---------------------------------------------------- +// lasmask state machine. +//---------------------------------------------------- +assign beg = (req3 | req2 | req1 | req0) & ~lcomreq; +always @ (posedge clk) +begin + lasmask <= (beg & ~ledge & ~lasmask); + ledge <= (beg & ~ledge & lasmask) + | (beg & ledge & ~lasmask); +end + +//---------------------------------------------------- +// comreq logic. +//---------------------------------------------------- +assign lcomreq = ( req3 & lgnt3 ) + | ( req2 & lgnt2 ) + | ( req1 & lgnt1 ) + | ( req0 & lgnt0 ); + +//---------------------------------------------------- +// Encoder logic. +//---------------------------------------------------- +assign lgnt = {(lgnt3 | lgnt2),(lgnt3 | lgnt1)}; + +//---------------------------------------------------- +// lmask register. +//---------------------------------------------------- +always @ (posedge clk ) +if( rst ) begin + lmask1 <= 0; + lmask0 <= 0; +end else if(lasmask) begin + lmask1 <= lgnt[1]; + lmask0 <= lgnt[0]; +end else begin + lmask1 <= lmask1; + lmask0 <= lmask0; +end + +assign comreq = lcomreq; +assign gnt = lgnt; +//---------------------------------------------------- +// Drive the outputs +//---------------------------------------------------- +assign gnt3 = lgnt3; +assign gnt2 = lgnt2; +assign gnt1 = lgnt1; +assign gnt0 = lgnt0; + +endmodule diff --git a/tests/asicworld/code_hdl_models_arbiter_tb.v b/tests/asicworld/code_hdl_models_arbiter_tb.v index 5e7bf46b9..78d1168e6 100644 --- a/tests/asicworld/code_hdl_models_arbiter_tb.v +++ b/tests/asicworld/code_hdl_models_arbiter_tb.v @@ -1,11 +1,11 @@ module testbench (); -reg clk; -reg rst; -reg req3; -reg req2; -reg req1; -reg req0; +reg clk = 0; +reg rst = 1; +reg req3 = 0; +reg req2 = 0; +reg req1 = 0; +reg req0 = 0; wire gnt3; wire gnt2; wire gnt1; @@ -13,17 +13,15 @@ wire gnt0; // Clock generator always #1 clk = ~clk; +integer file; + +always @(posedge clk) + $fdisplay(file, "%b", {gnt3, gnt2, gnt1, gnt0}); initial begin - $dumpfile ("arbiter.vcd"); - $dumpvars(); - clk = 0; - rst = 1; - req0 = 0; - req1 = 0; - req2 = 0; - req3 = 0; - #10 rst = 0; + file = $fopen(`outfile); + repeat (5) @ (posedge clk); + rst <= 0; repeat (1) @ (posedge clk); req0 <= 1; repeat (1) @ (posedge clk); diff --git a/tests/asicworld/code_hdl_models_dlatch_reset.v b/tests/asicworld/code_hdl_models_dlatch_reset.v deleted file mode 100644 index 2cfc6fbd8..000000000 --- a/tests/asicworld/code_hdl_models_dlatch_reset.v +++ /dev/null @@ -1,30 +0,0 @@ -//----------------------------------------------------- -// Design Name : dlatch_reset -// File Name : dlatch_reset.v -// Function : DLATCH async reset -// Coder : Deepak Kumar Tala -//----------------------------------------------------- -module dlatch_reset ( -data , // Data Input -en , // LatchInput -reset , // Reset input -q // Q output -); -//-----------Input Ports--------------- -input data, en, reset ; - -//-----------Output Ports--------------- -output q; - -//------------Internal Variables-------- -reg q; - -//-------------Code Starts Here--------- -always @ ( en or reset or data) -if (~reset) begin - q <= 1'b0; -end else if (en) begin - q <= data; -end - -endmodule //End Of Module dlatch_reset diff --git a/tests/asicworld/code_hdl_models_ram_sp_ar_sw.v b/tests/asicworld/code_hdl_models_ram_sp_ar_sw.v deleted file mode 100644 index d3338f749..000000000 --- a/tests/asicworld/code_hdl_models_ram_sp_ar_sw.v +++ /dev/null @@ -1,58 +0,0 @@ -//----------------------------------------------------- -// Design Name : ram_sp_ar_sw -// File Name : ram_sp_ar_sw.v -// Function : Asynchronous read write RAM -// Coder : Deepak Kumar Tala -//----------------------------------------------------- -module ram_sp_ar_sw ( -clk , // Clock Input -address , // Address Input -data , // Data bi-directional -cs , // Chip Select -we , // Write Enable/Read Enable -oe // Output Enable -); - -parameter DATA_WIDTH = 8 ; -parameter ADDR_WIDTH = 8 ; -parameter RAM_DEPTH = 1 << ADDR_WIDTH; - -//--------------Input Ports----------------------- -input clk ; -input [ADDR_WIDTH-1:0] address ; -input cs ; -input we ; -input oe ; - -//--------------Inout Ports----------------------- -inout [DATA_WIDTH-1:0] data ; - -//--------------Internal variables---------------- -reg [DATA_WIDTH-1:0] data_out ; -reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; - -//--------------Code Starts Here------------------ - -// Tri-State Buffer control -// output : When we = 0, oe = 1, cs = 1 -assign data = (cs && oe && !we) ? data_out : 8'bz; - -// Memory Write Block -// Write Operation : When we = 1, cs = 1 -always @ (posedge clk) -begin : MEM_WRITE - if ( cs && we ) begin - mem[address] = data; - end -end - -// Memory Read Block -// Read Operation : When we = 0, oe = 1, cs = 1 -always @ (address or cs or we or oe) -begin : MEM_READ - if (cs && !we && oe) begin - data_out = mem[address]; - end -end - -endmodule // End of Module ram_sp_ar_sw diff --git a/tests/asicworld/code_hdl_models_ram_sp_sr_sw.v b/tests/asicworld/code_hdl_models_ram_sp_sr_sw.v deleted file mode 100644 index c7fd9554d..000000000 --- a/tests/asicworld/code_hdl_models_ram_sp_sr_sw.v +++ /dev/null @@ -1,62 +0,0 @@ -//----------------------------------------------------- -// Design Name : ram_sp_sr_sw -// File Name : ram_sp_sr_sw.v -// Function : Synchronous read write RAM -// Coder : Deepak Kumar Tala -//----------------------------------------------------- -module ram_sp_sr_sw ( -clk , // Clock Input -address , // Address Input -data , // Data bi-directional -cs , // Chip Select -we , // Write Enable/Read Enable -oe // Output Enable -); - -parameter DATA_WIDTH = 8 ; -parameter ADDR_WIDTH = 8 ; -parameter RAM_DEPTH = 1 << ADDR_WIDTH; - -//--------------Input Ports----------------------- -input clk ; -input [ADDR_WIDTH-1:0] address ; -input cs ; -input we ; -input oe ; - -//--------------Inout Ports----------------------- -inout [DATA_WIDTH-1:0] data ; - -//--------------Internal variables---------------- -reg [DATA_WIDTH-1:0] data_out ; -reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; -reg oe_r; - -//--------------Code Starts Here------------------ - -// Tri-State Buffer control -// output : When we = 0, oe = 1, cs = 1 -assign data = (cs && oe && !we) ? data_out : 8'bz; - -// Memory Write Block -// Write Operation : When we = 1, cs = 1 -always @ (posedge clk) -begin : MEM_WRITE - if ( cs && we ) begin - mem[address] = data; - end -end - -// Memory Read Block -// Read Operation : When we = 0, oe = 1, cs = 1 -always @ (posedge clk) -begin : MEM_READ - if (cs && !we && oe) begin - data_out = mem[address]; - oe_r = 1; - end else begin - oe_r = 0; - end -end - -endmodule // End of Module ram_sp_sr_sw diff --git a/tests/asicworld/code_hdl_models_t_gate_switch.v b/tests/asicworld/code_hdl_models_t_gate_switch.v index 1bff66af8..5a7e0eaff 100644 --- a/tests/asicworld/code_hdl_models_t_gate_switch.v +++ b/tests/asicworld/code_hdl_models_t_gate_switch.v @@ -1,11 +1,11 @@ -module t_gate_switch (L,R,nC,C);
- inout L;
- inout R;
- input nC;
- input C;
-
- //Syntax: keyword unique_name (drain. source, gate);
- pmos p1 (L,R,nC);
- nmos p2 (L,R,C);
-
-endmodule
+module t_gate_switch (L,R,nC,C); + inout L; + inout R; + input nC; + input C; + + //Syntax: keyword unique_name (drain. source, gate); + pmos p1 (L,R,nC); + nmos p2 (L,R,C); + +endmodule diff --git a/tests/asicworld/code_hdl_models_up_counter.v b/tests/asicworld/code_hdl_models_up_counter.v index ffe670994..e05302182 100644 --- a/tests/asicworld/code_hdl_models_up_counter.v +++ b/tests/asicworld/code_hdl_models_up_counter.v @@ -2,7 +2,7 @@ // Design Name : up_counter // File Name : up_counter.v // Function : Up counter -// Coder : Deepak +// Coder : Deepak //----------------------------------------------------- module up_counter ( out , // Output of the counter diff --git a/tests/asicworld/code_verilog_tutorial_counter.v b/tests/asicworld/code_verilog_tutorial_counter.v index 534519745..10ca00df4 100644 --- a/tests/asicworld/code_verilog_tutorial_counter.v +++ b/tests/asicworld/code_verilog_tutorial_counter.v @@ -1,19 +1,19 @@ -//-----------------------------------------------------
-// Design Name : counter
-// File Name : counter.v
-// Function : 4 bit up counter
-// Coder : Deepak
-//-----------------------------------------------------
-module counter (clk, reset, enable, count);
-input clk, reset, enable;
-output [3:0] count;
-reg [3:0] count;
-
-always @ (posedge clk)
-if (reset == 1'b1) begin
- count <= 0;
-end else if ( enable == 1'b1) begin
- count <= count + 1;
-end
-
-endmodule
+//----------------------------------------------------- +// Design Name : counter +// File Name : counter.v +// Function : 4 bit up counter +// Coder : Deepak +//----------------------------------------------------- +module counter (clk, reset, enable, count); +input clk, reset, enable; +output [3:0] count; +reg [3:0] count; + +always @ (posedge clk) +if (reset == 1'b1) begin + count <= 0; +end else if ( enable == 1'b1) begin + count <= count + 1; +end + +endmodule diff --git a/tests/asicworld/code_verilog_tutorial_counter_tb.v b/tests/asicworld/code_verilog_tutorial_counter_tb.v index 104779381..33d540509 100644 --- a/tests/asicworld/code_verilog_tutorial_counter_tb.v +++ b/tests/asicworld/code_verilog_tutorial_counter_tb.v @@ -1,113 +1,101 @@ -///////////////////////////////////////////////////////////////////////////
-// MODULE : counter_tb //
-// TOP MODULE : -- //
-// //
-// PURPOSE : 4-bit up counter test bench //
-// //
-// DESIGNER : Deepak Kumar Tala //
-// //
-// Revision History //
-// //
-// DEVELOPMENT HISTORY : //
-// Rev0.0 : Jan 03, 2003 //
-// Initial Revision //
-// //
-///////////////////////////////////////////////////////////////////////////
-module testbench;
-
-reg clk, reset, enable;
-wire [3:0] count;
-reg dut_error;
-
-counter U0 (
-.clk (clk),
-.reset (reset),
-.enable (enable),
-.count (count)
-);
-
-event reset_enable;
-event terminate_sim;
-
-initial
-begin
- $display ("###################################################");
- clk = 0;
- reset = 0;
- enable = 0;
- dut_error = 0;
-end
-
-always
- #5 clk = !clk;
-
-initial
-begin
- $dumpfile ("counter.vcd");
- $dumpvars;
-end
-
-
-initial
-@ (terminate_sim) begin
- $display ("Terminating simulation");
- if (dut_error == 0) begin
- $display ("Simulation Result : PASSED");
- end
- else begin
- $display ("Simulation Result : FAILED");
- end
- $display ("###################################################");
- #1 $finish;
-end
-
-
-
-event reset_done;
-
-initial
-forever begin
- @ (reset_enable);
- @ (negedge clk)
- $display ("Applying reset");
- reset = 1;
- @ (negedge clk)
- reset = 0;
- $display ("Came out of Reset");
- -> reset_done;
-end
-
-initial begin
- #10 -> reset_enable;
- @ (reset_done);
- @ (negedge clk);
- enable = 1;
- repeat (5)
- begin
- @ (negedge clk);
- end
- enable = 0;
- #5 -> terminate_sim;
-end
-
-
-reg [3:0] count_compare;
-
-always @ (posedge clk)
-if (reset == 1'b1)
- count_compare <= 0;
-else if ( enable == 1'b1)
- count_compare <= count_compare + 1;
-
-
-
-always @ (negedge clk)
-if (count_compare != count) begin
- $display ("DUT ERROR AT TIME%d",$time);
- $display ("Expected value %d, Got Value %d", count_compare, count);
- dut_error = 1;
- #5 -> terminate_sim;
-end
-
-endmodule
-
+/////////////////////////////////////////////////////////////////////////// +// MODULE : counter_tb // +// TOP MODULE : -- // +// // +// PURPOSE : 4-bit up counter test bench // +// // +// DESIGNER : Deepak Kumar Tala // +// // +// Revision History // +// // +// DEVELOPMENT HISTORY : // +// Rev0.0 : Jan 03, 2003 // +// Initial Revision // +// // +/////////////////////////////////////////////////////////////////////////// +module testbench; + +integer file; +reg clk = 0, reset = 0, enable = 0; +wire [3:0] count; +reg dut_error = 0; + +counter U0 ( +.clk (clk), +.reset (reset), +.enable (enable), +.count (count) +); + +event reset_enable; +event terminate_sim; + +initial + file = $fopen(`outfile); + +always + #5 clk = !clk; + +initial +@ (terminate_sim) begin + $fdisplay (file, "Terminating simulation"); + if (dut_error == 0) begin + $fdisplay (file, "Simulation Result : PASSED"); + end + else begin + $fdisplay (file, "Simulation Result : FAILED"); + end + $fdisplay (file, "###################################################"); + #1 $finish; +end + + + +event reset_done; + +initial +forever begin + @ (reset_enable); + @ (negedge clk) + $fdisplay (file, "Applying reset"); + reset = 1; + @ (negedge clk) + reset = 0; + $fdisplay (file, "Came out of Reset"); + -> reset_done; +end + +initial begin + #10 -> reset_enable; + @ (reset_done); + @ (negedge clk); + enable = 1; + repeat (5) + begin + @ (negedge clk); + end + enable = 0; + #5 -> terminate_sim; +end + + +reg [3:0] count_compare; + +always @ (posedge clk) +if (reset == 1'b1) + count_compare <= 0; +else if ( enable == 1'b1) + count_compare <= count_compare + 1; + + + +always @ (negedge clk) +if (count_compare != count) begin + $fdisplay (file, "DUT ERROR AT TIME%d",$time); + $fdisplay (file, "Expected value %d, Got Value %d", count_compare, count); + dut_error = 1; + #5 -> terminate_sim; +end + +endmodule + diff --git a/tests/asicworld/code_verilog_tutorial_first_counter_tb.v b/tests/asicworld/code_verilog_tutorial_first_counter_tb.v index f065732be..806e17736 100644 --- a/tests/asicworld/code_verilog_tutorial_first_counter_tb.v +++ b/tests/asicworld/code_verilog_tutorial_first_counter_tb.v @@ -1,16 +1,13 @@ module testbench(); // Declare inputs as regs and outputs as wires -reg clock, reset, enable; +reg clock = 1, reset = 0, enable = 0; wire [3:0] counter_out; +integer file; // Initialize all variables initial begin - $display ("time\t clk reset enable counter"); - $monitor ("%g\t %b %b %b %b", - $time, clock, reset, enable, counter_out); - clock = 1; // initial value of clock - reset = 0; // initial value of reset - enable = 0; // initial value of enable + file = $fopen(`outfile); + $fdisplay (file, "time\t clk reset enable counter"); #5 reset = 1; // Assert the reset #10 reset = 0; // De-assert the reset #10 enable = 1; // Assert enable @@ -18,6 +15,10 @@ initial begin #5 $finish; // Terminate simulation end +always @(negedge clock) + $fdisplay (file, "%g\t %b %b %b %b", + $time, clock, reset, enable, counter_out); + // Clock generator initial begin #1; diff --git a/tests/asicworld/code_verilog_tutorial_fsm_full_tb.v b/tests/asicworld/code_verilog_tutorial_fsm_full_tb.v index 2e9448950..a8e15568b 100644 --- a/tests/asicworld/code_verilog_tutorial_fsm_full_tb.v +++ b/tests/asicworld/code_verilog_tutorial_fsm_full_tb.v @@ -1,14 +1,14 @@ module testbench(); -reg clock , reset ; +reg clock = 0 , reset ; reg req_0 , req_1 , req_2 , req_3; wire gnt_0 , gnt_1 , gnt_2 , gnt_3 ; +integer file; initial begin // $dumpfile("testbench.vcd"); // $dumpvars(0, testbench); - $display("Time\t R0 R1 R2 R3 G0 G1 G2 G3"); - $monitor("%g\t %b %b %b %b %b %b %b %b", - $time, req_0, req_1, req_2, req_3, gnt_0, gnt_1, gnt_2, gnt_3); + file = $fopen(`outfile); + $fdisplay(file, "Time\t R0 R1 R2 R3 G0 G1 G2 G3"); clock = 0; reset = 1; req_0 = 0; @@ -28,6 +28,10 @@ initial begin #10 $finish; end +always @(negedge clock) + $fdisplay(file, "%g\t %b %b %b %b %b %b %b %b", + $time, req_0, req_1, req_2, req_3, gnt_0, gnt_1, gnt_2, gnt_3); + initial begin #1; forever diff --git a/tests/bram/generate.py b/tests/bram/generate.py index 2adfdcfb0..05a7ed027 100644 --- a/tests/bram/generate.py +++ b/tests/bram/generate.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - -from __future__ import division -from __future__ import print_function +#!/usr/bin/env python3 import os import sys @@ -250,10 +247,10 @@ print("Rng seed: %d" % seed) random.seed(seed) for k1 in range(5): - dsc_f = file("temp/brams_%02d.txt" % k1, "w") - sim_f = file("temp/brams_%02d.v" % k1, "w") - ref_f = file("temp/brams_%02d_ref.v" % k1, "w") - tb_f = file("temp/brams_%02d_tb.v" % k1, "w") + dsc_f = open("temp/brams_%02d.txt" % k1, "w") + sim_f = open("temp/brams_%02d.v" % k1, "w") + ref_f = open("temp/brams_%02d_ref.v" % k1, "w") + tb_f = open("temp/brams_%02d_tb.v" % k1, "w") for f in [sim_f, ref_f, tb_f]: print("`timescale 1 ns / 1 ns", file=f) diff --git a/tests/bram/run-test.sh b/tests/bram/run-test.sh index d617187ec..f0bf0131e 100755 --- a/tests/bram/run-test.sh +++ b/tests/bram/run-test.sh @@ -8,7 +8,7 @@ rm -rf temp mkdir -p temp echo "generating tests.." -python generate.py +python3 generate.py { echo -n "all:" diff --git a/tests/fsm/generate.py b/tests/fsm/generate.py index b5b4626df..8757d4741 100644 --- a/tests/fsm/generate.py +++ b/tests/fsm/generate.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - -from __future__ import division -from __future__ import print_function +#!/usr/bin/env python3 import sys import random @@ -34,76 +31,78 @@ def random_expr(variables): raise AssertionError for idx in range(50): - with file('temp/uut_%05d.v' % idx, 'w') as f, redirect_stdout(f): - rst2 = random.choice([False, True]) - if rst2: - print('module uut_%05d(clk, rst1, rst2, rst, a, b, c, x, y, z);' % (idx)) - print(' input clk, rst1, rst2;') - print(' output rst;') - print(' assign rst = rst1 || rst2;') - else: - print('module uut_%05d(clk, rst, a, b, c, x, y, z);' % (idx)) - print(' input clk, rst;') - variables=['a', 'b', 'c', 'x', 'y', 'z'] - print(' input%s [%d:0] a;' % (random.choice(['', ' signed']), random.randint(0, 31))) - print(' input%s [%d:0] b;' % (random.choice(['', ' signed']), random.randint(0, 31))) - print(' input%s [%d:0] c;' % (random.choice(['', ' signed']), random.randint(0, 31))) - print(' output reg%s [%d:0] x;' % (random.choice(['', ' signed']), random.randint(0, 31))) - print(' output reg%s [%d:0] y;' % (random.choice(['', ' signed']), random.randint(0, 31))) - print(' output reg%s [%d:0] z;' % (random.choice(['', ' signed']), random.randint(0, 31))) - state_bits = random.randint(5, 16); - print(' %sreg [%d:0] state;' % (random.choice(['', '(* fsm_encoding = "one-hot" *)', - '(* fsm_encoding = "binary" *)']), state_bits-1)) - states=[] - for i in range(random.randint(2, 10)): - n = random.randint(0, 2**state_bits-1) - if n not in states: - states.append(n) - print(' always @(posedge clk) begin') - print(' if (%s) begin' % ('rst1' if rst2 else 'rst')) - print(' x <= %d;' % random.randint(0, 2**31-1)) - print(' y <= %d;' % random.randint(0, 2**31-1)) - print(' z <= %d;' % random.randint(0, 2**31-1)) - print(' state <= %d;' % random.choice(states)) - print(' end else begin') - print(' case (state)') - for state in states: - print(' %d: begin' % state) - for var in ('x', 'y', 'z'): - print(' %s <= %s;' % (var, random_expr(variables))) - next_states = states[:] - for i in range(random.randint(0, len(states))): - next_state = random.choice(next_states) - next_states.remove(next_state) - print(' if ((%s) %s (%s)) state <= %s;' % (random_expr(variables), - random.choice(['<', '<=', '>=', '>']), random_expr(variables), next_state)) - print(' end') - print(' endcase') - if rst2: - print(' if (rst2) begin') - print(' x <= a;') - print(' y <= b;') - print(' z <= c;') - print(' state <= %d;' % random.choice(states)) - print(' end') - print(' end') - print(' end') - print('endmodule') - with file('temp/uut_%05d.ys' % idx, 'w') as f, redirect_stdout(f): - if test_verific: - print('read_verilog temp/uut_%05d.v' % idx) - print('proc;; rename uut_%05d gold' % idx) - print('verific -vlog2k temp/uut_%05d.v' % idx) - print('verific -import uut_%05d' % idx) - print('rename uut_%05d gate' % idx) - else: - print('read_verilog temp/uut_%05d.v' % idx) - print('proc;;') - print('copy uut_%05d gold' % idx) - print('rename uut_%05d gate' % idx) - print('cd gate') - print('opt; wreduce; share%s; opt; fsm;;' % random.choice(['', ' -aggressive'])) - print('cd ..') - print('miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp gold gate miter') - print('sat -verify-no-timeout -timeout 20 -seq 5 -set-at 1 %s_rst 1 -prove trigger 0 -prove-skip 1 -show-inputs -show-outputs miter' % ('gold' if rst2 else 'in')) - + with open('temp/uut_%05d.v' % idx, 'w') as f: + with redirect_stdout(f): + rst2 = random.choice([False, True]) + if rst2: + print('module uut_%05d(clk, rst1, rst2, rst, a, b, c, x, y, z);' % (idx)) + print(' input clk, rst1, rst2;') + print(' output rst;') + print(' assign rst = rst1 || rst2;') + else: + print('module uut_%05d(clk, rst, a, b, c, x, y, z);' % (idx)) + print(' input clk, rst;') + variables=['a', 'b', 'c', 'x', 'y', 'z'] + print(' input%s [%d:0] a;' % (random.choice(['', ' signed']), random.randint(0, 31))) + print(' input%s [%d:0] b;' % (random.choice(['', ' signed']), random.randint(0, 31))) + print(' input%s [%d:0] c;' % (random.choice(['', ' signed']), random.randint(0, 31))) + print(' output reg%s [%d:0] x;' % (random.choice(['', ' signed']), random.randint(0, 31))) + print(' output reg%s [%d:0] y;' % (random.choice(['', ' signed']), random.randint(0, 31))) + print(' output reg%s [%d:0] z;' % (random.choice(['', ' signed']), random.randint(0, 31))) + state_bits = random.randint(5, 16); + print(' %sreg [%d:0] state;' % (random.choice(['', '(* fsm_encoding = "one-hot" *)', + '(* fsm_encoding = "binary" *)']), state_bits-1)) + states=[] + for i in range(random.randint(2, 10)): + n = random.randint(0, 2**state_bits-1) + if n not in states: + states.append(n) + print(' always @(posedge clk) begin') + print(' if (%s) begin' % ('rst1' if rst2 else 'rst')) + print(' x <= %d;' % random.randint(0, 2**31-1)) + print(' y <= %d;' % random.randint(0, 2**31-1)) + print(' z <= %d;' % random.randint(0, 2**31-1)) + print(' state <= %d;' % random.choice(states)) + print(' end else begin') + print(' case (state)') + for state in states: + print(' %d: begin' % state) + for var in ('x', 'y', 'z'): + print(' %s <= %s;' % (var, random_expr(variables))) + next_states = states[:] + for i in range(random.randint(0, len(states))): + next_state = random.choice(next_states) + next_states.remove(next_state) + print(' if ((%s) %s (%s)) state <= %s;' % (random_expr(variables), + random.choice(['<', '<=', '>=', '>']), random_expr(variables), next_state)) + print(' end') + print(' endcase') + if rst2: + print(' if (rst2) begin') + print(' x <= a;') + print(' y <= b;') + print(' z <= c;') + print(' state <= %d;' % random.choice(states)) + print(' end') + print(' end') + print(' end') + print('endmodule') + with open('temp/uut_%05d.ys' % idx, 'w') as f: + with redirect_stdout(f): + if test_verific: + print('read_verilog temp/uut_%05d.v' % idx) + print('proc;; rename uut_%05d gold' % idx) + print('verific -vlog2k temp/uut_%05d.v' % idx) + print('verific -import uut_%05d' % idx) + print('rename uut_%05d gate' % idx) + else: + print('read_verilog temp/uut_%05d.v' % idx) + print('proc;;') + print('copy uut_%05d gold' % idx) + print('rename uut_%05d gate' % idx) + print('cd gate') + print('opt; wreduce; share%s; opt; fsm;;' % random.choice(['', ' -aggressive'])) + print('cd ..') + print('miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp gold gate miter') + print('sat -verify-no-timeout -timeout 20 -seq 5 -set-at 1 %s_rst 1 -prove trigger 0 -prove-skip 1 -show-inputs -show-outputs miter' % ('gold' if rst2 else 'in')) + diff --git a/tests/fsm/run-test.sh b/tests/fsm/run-test.sh index 57c2a5b12..423892334 100755 --- a/tests/fsm/run-test.sh +++ b/tests/fsm/run-test.sh @@ -8,7 +8,7 @@ set -e rm -rf temp mkdir -p temp echo "generating tests.." -python generate.py +python3 generate.py { all_targets="all_targets:" diff --git a/tests/hana/README b/tests/hana/README index b2a08fd47..2081fb10f 100644 --- a/tests/hana/README +++ b/tests/hana/README @@ -1,4 +1,4 @@ -This test cases are copied from the hana project: +These test cases are copied from the hana project: https://sourceforge.net/projects/sim-sim/ diff --git a/tests/realmath/generate.py b/tests/realmath/generate.py index 972021dc8..19d01c7c6 100644 --- a/tests/realmath/generate.py +++ b/tests/realmath/generate.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - -from __future__ import division -from __future__ import print_function +#!/usr/bin/env python3 import sys import random @@ -39,53 +36,56 @@ def random_expression(depth = 3, maxparam = 0): return op + '(' + recursion() + ', ' + recursion() + ')' raise -for idx in range(100): - with file('temp/uut_%05d.v' % idx, 'w') as f, redirect_stdout(f): - print('module uut_%05d(output [63:0] %s);\n' % (idx, ', '.join(['y%02d' % i for i in range(100)]))) - for i in range(30): - if idx < 10: - print('localparam p%02d = %s;' % (i, random_expression())) - else: - print('localparam%s p%02d = %s;' % (random.choice(['', ' real', ' integer']), i, random_expression())) - for i in range(30, 60): - if idx < 10: - print('localparam p%02d = %s;' % (i, random_expression(maxparam = 30))) - else: - print('localparam%s p%02d = %s;' % (random.choice(['', ' real', ' integer']), i, random_expression(maxparam = 30))) - for i in range(100): - print('assign y%02d = 65536 * (%s);' % (i, random_expression(maxparam = 60))) - print('endmodule') - with file('temp/uut_%05d.ys' % idx, 'w') as f, redirect_stdout(f): - print('read_verilog uut_%05d.v' % idx) - print('rename uut_%05d uut_%05d_syn' % (idx, idx)) - print('write_verilog uut_%05d_syn.v' % idx) - with file('temp/uut_%05d_tb.v' % idx, 'w') as f, redirect_stdout(f): - print('module uut_%05d_tb;\n' % idx) - print('wire [63:0] %s;' % (', '.join(['r%02d' % i for i in range(100)]))) - print('wire [63:0] %s;' % (', '.join(['s%02d' % i for i in range(100)]))) - print('uut_%05d ref(%s);' % (idx, ', '.join(['r%02d' % i for i in range(100)]))) - print('uut_%05d_syn syn(%s);' % (idx, ', '.join(['s%02d' % i for i in range(100)]))) - print('task compare_ref_syn;') - print(' input [7:0] i;') - print(' input [63:0] r, s;') - print(' reg [64*8-1:0] buffer;') - print(' integer j;') - print(' begin') - print(' if (-1 <= $signed(r-s) && $signed(r-s) <= +1) begin') - print(' // $display("%d: %b %b", i, r, s);') - print(' end else if (r === s) begin ') - print(' // $display("%d: %b %b", i, r, s);') - print(' end else begin ') - print(' for (j = 0; j < 64; j = j+1)') - print(' buffer[j*8 +: 8] = r[j] !== s[j] ? "^" : " ";') - print(' $display("\\n%3d: %b %b", i, r, s);') - print(' $display(" %s %s", buffer, buffer);') - print(' end') - print(' end') - print('endtask') - print('initial begin #1;') - for i in range(100): - print(' compare_ref_syn(%2d, r%02d, s%02d);' % (i, i, i)) - print('end') - print('endmodule') - +for idx in range(100): + with open('temp/uut_%05d.v' % idx, 'w') as f: + with redirect_stdout(f): + print('module uut_%05d(output [63:0] %s);\n' % (idx, ', '.join(['y%02d' % i for i in range(100)]))) + for i in range(30): + if idx < 10: + print('localparam p%02d = %s;' % (i, random_expression())) + else: + print('localparam%s p%02d = %s;' % (random.choice(['', ' real', ' integer']), i, random_expression())) + for i in range(30, 60): + if idx < 10: + print('localparam p%02d = %s;' % (i, random_expression(maxparam = 30))) + else: + print('localparam%s p%02d = %s;' % (random.choice(['', ' real', ' integer']), i, random_expression(maxparam = 30))) + for i in range(100): + print('assign y%02d = 65536 * (%s);' % (i, random_expression(maxparam = 60))) + print('endmodule') + with open('temp/uut_%05d.ys' % idx, 'w') as f: + with redirect_stdout(f): + print('read_verilog uut_%05d.v' % idx) + print('rename uut_%05d uut_%05d_syn' % (idx, idx)) + print('write_verilog uut_%05d_syn.v' % idx) + with open('temp/uut_%05d_tb.v' % idx, 'w') as f: + with redirect_stdout(f): + print('module uut_%05d_tb;\n' % idx) + print('wire [63:0] %s;' % (', '.join(['r%02d' % i for i in range(100)]))) + print('wire [63:0] %s;' % (', '.join(['s%02d' % i for i in range(100)]))) + print('uut_%05d ref(%s);' % (idx, ', '.join(['r%02d' % i for i in range(100)]))) + print('uut_%05d_syn syn(%s);' % (idx, ', '.join(['s%02d' % i for i in range(100)]))) + print('task compare_ref_syn;') + print(' input [7:0] i;') + print(' input [63:0] r, s;') + print(' reg [64*8-1:0] buffer;') + print(' integer j;') + print(' begin') + print(' if (-1 <= $signed(r-s) && $signed(r-s) <= +1) begin') + print(' // $display("%d: %b %b", i, r, s);') + print(' end else if (r === s) begin ') + print(' // $display("%d: %b %b", i, r, s);') + print(' end else begin ') + print(' for (j = 0; j < 64; j = j+1)') + print(' buffer[j*8 +: 8] = r[j] !== s[j] ? "^" : " ";') + print(' $display("\\n%3d: %b %b", i, r, s);') + print(' $display(" %s %s", buffer, buffer);') + print(' end') + print(' end') + print('endtask') + print('initial begin #1;') + for i in range(100): + print(' compare_ref_syn(%2d, r%02d, s%02d);' % (i, i, i)) + print('end') + print('endmodule') + diff --git a/tests/realmath/run-test.sh b/tests/realmath/run-test.sh index 0997ccb5d..f1ec5476b 100755 --- a/tests/realmath/run-test.sh +++ b/tests/realmath/run-test.sh @@ -4,7 +4,7 @@ set -e rm -rf temp mkdir -p temp echo "generating tests.." -python generate.py +python3 generate.py cd temp echo "running tests.." @@ -15,7 +15,7 @@ for ((i = 0; i < 100; i++)); do iverilog -o uut_${idx}_tb uut_${idx}_tb.v uut_${idx}.v uut_${idx}_syn.v ./uut_${idx}_tb | tee uut_${idx}.err if test -s uut_${idx}.err; then - echo "Note: Make sure that 'iverilog' is an up-to-date git checkout of icarus verilog." + echo "Note: Make sure that 'iverilog' is an up-to-date git checkout of Icarus Verilog." exit 1 fi rm -f uut_${idx}.err diff --git a/tests/share/generate.py b/tests/share/generate.py index a06a642d8..01a19a8d9 100644 --- a/tests/share/generate.py +++ b/tests/share/generate.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - -from __future__ import division -from __future__ import print_function +#!/usr/bin/env python3 import sys import random @@ -25,49 +22,51 @@ def maybe_plus_x(expr): return expr for idx in range(100): - with file('temp/uut_%05d.v' % idx, 'w') as f, redirect_stdout(f): - if random.choice(['bin', 'uni']) == 'bin': - print('module uut_%05d(a, b, c, d, x, s, y);' % (idx)) - op = random.choice([ - random.choice(['+', '-', '*', '/', '%']), - random.choice(['<', '<=', '==', '!=', '===', '!==', '>=', '>' ]), - random.choice(['<<', '>>', '<<<', '>>>']), - random.choice(['|', '&', '^', '~^', '||', '&&']), - ]) - print(' input%s [%d:0] a;' % (random.choice(['', ' signed']), random.randint(0, 8))) - print(' input%s [%d:0] b;' % (random.choice(['', ' signed']), random.randint(0, 8))) - print(' input%s [%d:0] c;' % (random.choice(['', ' signed']), random.randint(0, 8))) - print(' input%s [%d:0] d;' % (random.choice(['', ' signed']), random.randint(0, 8))) - print(' input%s [%d:0] x;' % (random.choice(['', ' signed']), random.randint(0, 8))) - print(' input s;') - print(' output [%d:0] y;' % random.randint(0, 8)) - print(' assign y = (s ? %s(%s %s %s) : %s(%s %s %s))%s;' % - (random.choice(['', '$signed', '$unsigned']), maybe_plus_x('a'), op, maybe_plus_x('b'), - random.choice(['', '$signed', '$unsigned']), maybe_plus_x('c'), op, maybe_plus_x('d'), - random_plus_x() if random.randint(0, 4) == 0 else '')) - print('endmodule') - else: - print('module uut_%05d(a, b, x, s, y);' % (idx)) - op = random.choice(['~', '-', '!']) - print(' input%s [%d:0] a;' % (random.choice(['', ' signed']), random.randint(0, 8))) - print(' input%s [%d:0] b;' % (random.choice(['', ' signed']), random.randint(0, 8))) - print(' input%s [%d:0] x;' % (random.choice(['', ' signed']), random.randint(0, 8))) - print(' input s;') - print(' output [%d:0] y;' % random.randint(0, 8)) - print(' assign y = (s ? %s(%s%s) : %s(%s%s))%s;' % - (random.choice(['', '$signed', '$unsigned']), op, maybe_plus_x('a'), - random.choice(['', '$signed', '$unsigned']), op, maybe_plus_x('b'), - random_plus_x() if random.randint(0, 4) == 0 else '')) - print('endmodule') - with file('temp/uut_%05d.ys' % idx, 'w') as f, redirect_stdout(f): - print('read_verilog temp/uut_%05d.v' % idx) - print('proc;;') - print('copy uut_%05d gold' % idx) - print('rename uut_%05d gate' % idx) - print('tee -a temp/all_share_log.txt log') - print('tee -a temp/all_share_log.txt log #job# uut_%05d' % idx) - print('tee -a temp/all_share_log.txt wreduce') - print('tee -a temp/all_share_log.txt share -aggressive gate') - print('miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp gold gate miter') - print('sat -set-def-inputs -verify -prove trigger 0 -show-inputs -show-outputs miter') - + with open('temp/uut_%05d.v' % idx, 'w') as f: + with redirect_stdout(f): + if random.choice(['bin', 'uni']) == 'bin': + print('module uut_%05d(a, b, c, d, x, s, y);' % (idx)) + op = random.choice([ + random.choice(['+', '-', '*', '/', '%']), + random.choice(['<', '<=', '==', '!=', '===', '!==', '>=', '>' ]), + random.choice(['<<', '>>', '<<<', '>>>']), + random.choice(['|', '&', '^', '~^', '||', '&&']), + ]) + print(' input%s [%d:0] a;' % (random.choice(['', ' signed']), random.randint(0, 8))) + print(' input%s [%d:0] b;' % (random.choice(['', ' signed']), random.randint(0, 8))) + print(' input%s [%d:0] c;' % (random.choice(['', ' signed']), random.randint(0, 8))) + print(' input%s [%d:0] d;' % (random.choice(['', ' signed']), random.randint(0, 8))) + print(' input%s [%d:0] x;' % (random.choice(['', ' signed']), random.randint(0, 8))) + print(' input s;') + print(' output [%d:0] y;' % random.randint(0, 8)) + print(' assign y = (s ? %s(%s %s %s) : %s(%s %s %s))%s;' % + (random.choice(['', '$signed', '$unsigned']), maybe_plus_x('a'), op, maybe_plus_x('b'), + random.choice(['', '$signed', '$unsigned']), maybe_plus_x('c'), op, maybe_plus_x('d'), + random_plus_x() if random.randint(0, 4) == 0 else '')) + print('endmodule') + else: + print('module uut_%05d(a, b, x, s, y);' % (idx)) + op = random.choice(['~', '-', '!']) + print(' input%s [%d:0] a;' % (random.choice(['', ' signed']), random.randint(0, 8))) + print(' input%s [%d:0] b;' % (random.choice(['', ' signed']), random.randint(0, 8))) + print(' input%s [%d:0] x;' % (random.choice(['', ' signed']), random.randint(0, 8))) + print(' input s;') + print(' output [%d:0] y;' % random.randint(0, 8)) + print(' assign y = (s ? %s(%s%s) : %s(%s%s))%s;' % + (random.choice(['', '$signed', '$unsigned']), op, maybe_plus_x('a'), + random.choice(['', '$signed', '$unsigned']), op, maybe_plus_x('b'), + random_plus_x() if random.randint(0, 4) == 0 else '')) + print('endmodule') + with open('temp/uut_%05d.ys' % idx, 'w') as f: + with redirect_stdout(f): + print('read_verilog temp/uut_%05d.v' % idx) + print('proc;;') + print('copy uut_%05d gold' % idx) + print('rename uut_%05d gate' % idx) + print('tee -a temp/all_share_log.txt log') + print('tee -a temp/all_share_log.txt log #job# uut_%05d' % idx) + print('tee -a temp/all_share_log.txt wreduce') + print('tee -a temp/all_share_log.txt share -aggressive gate') + print('miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp gold gate miter') + print('sat -set-def-inputs -verify -prove trigger 0 -show-inputs -show-outputs miter') + diff --git a/tests/share/run-test.sh b/tests/share/run-test.sh index 6e880677c..18dbbc279 100755 --- a/tests/share/run-test.sh +++ b/tests/share/run-test.sh @@ -8,7 +8,7 @@ set -e rm -rf temp mkdir -p temp echo "generating tests.." -python generate.py +python3 generate.py echo "running tests.." for i in $( ls temp/*.ys | sed 's,[^0-9],,g; s,^0*\(.\),\1,g;' ); do diff --git a/tests/simple/constmuldivmod.v b/tests/simple/constmuldivmod.v new file mode 100644 index 000000000..d1d8be862 --- /dev/null +++ b/tests/simple/constmuldivmod.v @@ -0,0 +1,27 @@ +module constmuldivmod(input [7:0] A, input [2:0] mode, output reg [7:0] Y); + always @* begin + case (mode) + 0: Y = A / 8'd0; + 1: Y = A % 8'd0; + 2: Y = A * 8'd0; + + 3: Y = A / 8'd1; + 4: Y = A % 8'd1; + 5: Y = A * 8'd1; + + 6: Y = A / 8'd2; + 7: Y = A % 8'd2; + 8: Y = A * 8'd2; + + 9: Y = A / 8'd4; + 10: Y = A % 8'd4; + 11: Y = A * 8'd4; + + 12: Y = A / 8'd8; + 13: Y = A % 8'd8; + 14: Y = A * 8'd8; + + default: Y = 8'd16 * A; + endcase + end +endmodule diff --git a/tests/simple/dff_different_styles.v b/tests/simple/dff_different_styles.v index 2f2737c4c..7765d6e2a 100644 --- a/tests/simple/dff_different_styles.v +++ b/tests/simple/dff_different_styles.v @@ -65,7 +65,7 @@ always @(posedge clk, posedge arst1, posedge arst2, negedge arst3) begin end endmodule -// SR-Flip-Flops are on the edge of well defined vewrilog constructs in terms of +// SR-Flip-Flops are on the edge of well defined Verilog constructs in terms of // simulation-implementation mismatches. The following testcases try to cover the // part that is defined and avoid the undefined cases. diff --git a/tests/simple/graphtest.v b/tests/simple/graphtest.v new file mode 100644 index 000000000..74788dbbe --- /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/hierarchy.v b/tests/simple/hierarchy.v index 17888009f..123afaeab 100644 --- a/tests/simple/hierarchy.v +++ b/tests/simple/hierarchy.v @@ -5,10 +5,10 @@ input [3:0] a; input signed [3:0] b; output [7:0] y1, y2, y3, y4; -// this version triggers a bug in icarus verilog +// this version triggers a bug in Icarus Verilog // submod #(-3'sd1, 3'b111 + 3'b001) foo (a, b, y1, y2, y3, y4); -// this version is handled correctly by icarus verilog +// this version is handled correctly by Icarus Verilog submod #(-3'sd1, -3'sd1) foo (a, b, y1, y2, y3, y4); endmodule diff --git a/tests/simple/loops.v b/tests/simple/loops.v index 77cdcd8e2..d7743a422 100644 --- a/tests/simple/loops.v +++ b/tests/simple/loops.v @@ -41,10 +41,10 @@ begin keysched_last_key_i = key_i; else keysched_last_key_i = keysched_new_key_o; - + if (round == 0 && addroundkey_start_i) begin - data_var = addroundkey_data_i; + data_var = addroundkey_data_i; round_key_var = key_i; round_data_var = round_key_var ^ data_var; next_addroundkey_data_reg = round_data_var; @@ -66,7 +66,7 @@ begin end else if (addroundkey_round == round && keysched_ready_o) begin - data_var = addroundkey_data_i; + data_var = addroundkey_data_i; round_key_var = keysched_new_key_o; round_data_var = round_key_var ^ data_var; next_addroundkey_data_reg = round_data_var; diff --git a/tests/simple/mem2reg.v b/tests/simple/mem2reg.v index bed5528d4..b1ab04d62 100644 --- a/tests/simple/mem2reg.v +++ b/tests/simple/mem2reg.v @@ -19,9 +19,9 @@ endmodule // ------------------------------------------------------ -module mem2reg_test2(clk, mode, addr, data); +module mem2reg_test2(clk, reset, mode, addr, data); -input clk, mode; +input clk, reset, mode; input [2:0] addr; output [3:0] data; @@ -33,6 +33,10 @@ assign data = mem[addr]; integer i; always @(posedge clk) begin + if (reset) begin + for (i=0; i<8; i=i+1) + mem[i] <= i; + end else if (mode) begin for (i=0; i<8; i=i+1) mem[i] <= mem[i]+1; @@ -47,7 +51,7 @@ endmodule // http://www.reddit.com/r/yosys/comments/28d9lx/problem_with_concatenation_of_two_dimensional/ module mem2reg_test3( input clk, input [8:0] din_a, output reg [7:0] dout_a, output [7:0] dout_b); -reg [7:0] dint_c [0:7]; +reg [7:0] dint_c [0:7]; always @(posedge clk) begin {dout_a[0], dint_c[3]} <= din_a; diff --git a/tests/simple/memory.v b/tests/simple/memory.v index db06c56d2..9fddce26c 100644 --- a/tests/simple/memory.v +++ b/tests/simple/memory.v @@ -205,3 +205,62 @@ module memtest08(input clk, input [3:0] a, b, c, output reg [3:0] y); end endmodule +// ---------------------------------------------------------- + +module memtest09 ( + input clk, + input [3:0] a_addr, a_din, b_addr, b_din, + input a_wen, b_wen, + output reg [3:0] a_dout, b_dout +); + reg [3:0] memory [10:35]; + + always @(posedge clk) begin + if (a_wen) + memory[10 + a_addr] <= a_din; + a_dout <= memory[10 + a_addr]; + end + + always @(posedge clk) begin + if (b_wen && (10 + a_addr != 20 + b_addr || !a_wen)) + memory[20 + b_addr] <= b_din; + b_dout <= memory[20 + b_addr]; + 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 + +// ---------------------------------------------------------- + +module memtest11(clk, wen, waddr, raddr, wdata, rdata); + input clk, wen; + input [1:0] waddr, raddr; + input [7:0] wdata; + output [7:0] rdata; + + reg [7:0] mem [3:0]; + + assign rdata = mem[raddr]; + + always @(posedge clk) begin + if (wen) + mem[waddr] <= wdata; + else + mem[waddr] <= mem[waddr]; + end +endmodule + diff --git a/tests/simple/omsp_dbg_uart.v b/tests/simple/omsp_dbg_uart.v index dc8860dee..569a28adb 100644 --- a/tests/simple/omsp_dbg_uart.v +++ b/tests/simple/omsp_dbg_uart.v @@ -22,13 +22,13 @@ always @(uart_state or mem_burst) RX_DATA : uart_state_nxt = RX_SYNC; default : uart_state_nxt = RX_CMD; endcase - + always @(posedge dbg_clk or posedge dbg_rst) if (dbg_rst) uart_state <= RX_SYNC; else if (xfer_done | mem_burst) uart_state <= uart_state_nxt; assign cmd_valid = (uart_state==RX_CMD) & xfer_done; assign xfer_done = uart_state!=RX_SYNC; - + endmodule diff --git a/tests/simple/rotate.v b/tests/simple/rotate.v index eb832e6f5..a2fe00055 100644 --- a/tests/simple/rotate.v +++ b/tests/simple/rotate.v @@ -1,5 +1,5 @@ -// test case taken from amber23 verilog code +// test case taken from amber23 Verilog code module a23_barrel_shift_fpga_rotate(i_in, direction, shift_amount, rot_prod); input [31:0] i_in; diff --git a/tests/simple/task_func.v b/tests/simple/task_func.v index 9b8e26e51..fa50c1d5c 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/vloghammer.v b/tests/simple/vloghammer.v index d1f55fdb4..3bb3cf992 100644 --- a/tests/simple/vloghammer.v +++ b/tests/simple/vloghammer.v @@ -27,14 +27,14 @@ module test04(a, y); assign y = ~(a - 1'b0); endmodule -// .. this test triggers a bug in xilinx isim. +// .. this test triggers a bug in Xilinx ISIM. // module test05(a, y); // input a; // output y; // assign y = 12345 >> {a, 32'd0}; // endmodule -// .. this test triggers a bug in icarus verilog. +// .. this test triggers a bug in Icarus Verilog. // module test06(a, b, c, y); // input signed [3:0] a; // input signed [1:0] b; diff --git a/tests/simple/wreduce.v b/tests/simple/wreduce.v new file mode 100644 index 000000000..ba5484385 --- /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 diff --git a/tests/smv/.gitignore b/tests/smv/.gitignore new file mode 100644 index 000000000..9c595a6fb --- /dev/null +++ b/tests/smv/.gitignore @@ -0,0 +1 @@ +temp diff --git a/tests/smv/run-single.sh b/tests/smv/run-single.sh new file mode 100644 index 000000000..a261f4ea6 --- /dev/null +++ b/tests/smv/run-single.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +cat > $1.tpl <<EOT +%module main + INVARSPEC ! bool(_trigger) +EOT + +cat > $1.ys <<EOT +echo on + +read_ilang $1.il +hierarchy; proc; opt +rename -top uut +design -save gold + +synth +design -stash gate + +design -copy-from gold -as gold uut +design -copy-from gate -as gate uut +miter -equiv -flatten gold gate main +hierarchy -top main + +dump +write_smv -tpl $1.tpl $1.smv +EOT + +set -ex + +../../yosys -l $1.log -q $1.ys +NuSMV -bmc $1.smv >> $1.log +grep "^-- invariant .* is true" $1.log + diff --git a/tests/smv/run-test.sh b/tests/smv/run-test.sh new file mode 100755 index 000000000..74a54486d --- /dev/null +++ b/tests/smv/run-test.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -ex + +rm -rf temp +mkdir -p temp + +../../yosys -p 'test_cell -muxdiv -w temp/test all' +rm -f temp/test_{alu,fa,lcu,lut,macc,shiftx}_* + +cat > temp/makefile << "EOT" +all: $(addsuffix .ok,$(basename $(wildcard temp/test_*.il))) +%.ok: %.il + bash run-single.sh $(basename $<) + touch $@ +EOT + +${MAKE:-make} -f temp/makefile + diff --git a/tests/techmap/mem_simple_4x1_map.v b/tests/techmap/mem_simple_4x1_map.v index 820f89de4..762e2938e 100644 --- a/tests/techmap/mem_simple_4x1_map.v +++ b/tests/techmap/mem_simple_4x1_map.v @@ -1,10 +1,11 @@ -module \$mem (RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA); +module \$mem (RD_CLK, RD_EN, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA); parameter MEMID = ""; parameter SIZE = 256; parameter OFFSET = 0; parameter ABITS = 8; parameter WIDTH = 8; + parameter signed INIT = 1'bx; parameter RD_PORTS = 1; parameter RD_CLK_ENABLE = 1'b1; @@ -16,6 +17,7 @@ module \$mem (RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA); parameter WR_CLK_POLARITY = 1'b1; input [RD_PORTS-1:0] RD_CLK; + input [RD_PORTS-1:0] RD_EN; input [RD_PORTS*ABITS-1:0] RD_ADDR; output reg [RD_PORTS*WIDTH-1:0] RD_DATA; @@ -29,6 +31,8 @@ module \$mem (RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA); parameter _TECHMAP_CONNMAP_RD_CLK_ = 0; parameter _TECHMAP_CONNMAP_WR_CLK_ = 0; + parameter _TECHMAP_CONSTVAL_RD_EN_ = 0; + parameter _TECHMAP_BITS_CONNMAP_ = 0; parameter _TECHMAP_CONNMAP_WR_EN_ = 0; @@ -37,10 +41,18 @@ module \$mem (RD_CLK, RD_ADDR, RD_DATA, WR_CLK, WR_EN, WR_ADDR, WR_DATA); initial begin _TECHMAP_FAIL_ <= 0; + // no initialized memories + if (INIT !== 1'bx) + _TECHMAP_FAIL_ <= 1; + // only map cells with only one read and one write port if (RD_PORTS > 1 || WR_PORTS > 1) _TECHMAP_FAIL_ <= 1; + // read enable must be constant high + if (_TECHMAP_CONSTVAL_RD_EN_[0] !== 1'b1) + _TECHMAP_FAIL_ <= 1; + // we expect positive read clock and non-transparent reads if (RD_TRANSPARENT || !RD_CLK_ENABLE || !RD_CLK_POLARITY) _TECHMAP_FAIL_ <= 1; diff --git a/tests/tools/autotest.sh b/tests/tools/autotest.sh index 50f5cb580..82fecfd80 100755 --- a/tests/tools/autotest.sh +++ b/tests/tools/autotest.sh @@ -16,7 +16,7 @@ toolsdir="$(cd $(dirname $0); pwd)" warn_iverilog_git=false if [ ! -f $toolsdir/cmp_tbdata -o $toolsdir/cmp_tbdata.c -nt $toolsdir/cmp_tbdata ]; then - ( set -ex; gcc -Wall -o $toolsdir/cmp_tbdata $toolsdir/cmp_tbdata.c; ) || exit 1 + ( set -ex; ${CC:-gcc} -Wall -o $toolsdir/cmp_tbdata $toolsdir/cmp_tbdata.c; ) || exit 1 fi while getopts xmGl:wkjvref:s:p:n: opt; do @@ -56,28 +56,24 @@ while getopts xmGl:wkjvref:s:p:n: opt; do esac done -create_ref() { - cp "$1" "$2.v" -} - compile_and_run() { exe="$1"; output="$2"; shift 2 if $use_modelsim; then altver=$( ls -v /opt/altera/ | grep '^[0-9]' | tail -n1; ) /opt/altera/$altver/modelsim_ase/bin/vlib work - /opt/altera/$altver/modelsim_ase/bin/vlog "$@" - /opt/altera/$altver/modelsim_ase/bin/vsim -c -do 'run -all; exit;' testbench | grep '#OUT#' > "$output" + /opt/altera/$altver/modelsim_ase/bin/vlog +define+outfile=\"$output\" "$@" + /opt/altera/$altver/modelsim_ase/bin/vsim -c -do 'run -all; exit;' testbench elif $use_xsim; then ( set +x files=( "$@" ) xilver=$( ls -v /opt/Xilinx/Vivado/ | grep '^[0-9]' | tail -n1; ) - /opt/Xilinx/Vivado/$xilver/bin/xvlog "${files[@]}" - /opt/Xilinx/Vivado/$xilver/bin/xelab -R work.testbench | grep '#OUT#' > "$output" + /opt/Xilinx/Vivado/$xilver/bin/xvlog -d outfile=\"$output\" "${files[@]}" + /opt/Xilinx/Vivado/$xilver/bin/xelab -R work.testbench ) else - iverilog -s testbench -o "$exe" "$@" - vvp -n "$exe" > "$output" + iverilog -Doutfile=\"$output\" -s testbench -o "$exe" "$@" + vvp -n "$exe" fi } @@ -108,15 +104,15 @@ do fn=$(basename $fn) bn=$(basename $bn) - cp ../$fn $fn + egrep -v '^\s*`timescale' ../$fn > ${bn}_ref.v + if [ ! -f ../${bn}_tb.v ]; then - "$toolsdir"/../../yosys -b "test_autotb $autotb_opts" -o ${bn}_tb.v $fn + "$toolsdir"/../../yosys -b "test_autotb $autotb_opts" -o ${bn}_tb.v ${bn}_ref.v else cp ../${bn}_tb.v ${bn}_tb.v fi if $genvcd; then sed -i 's,// \$dump,$dump,g' ${bn}_tb.v; fi - create_ref $fn ${bn}_ref - compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.v $libs + compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.v $libs if $genvcd; then mv testbench.vcd ${bn}_ref.vcd; fi test_count=0 @@ -131,22 +127,22 @@ do test_count=$(( test_count + 1 )) } - if [ "$frontend" = "verific" -o "$frontend" = "verific_gates" ] && grep -q VERIFIC-SKIP $fn; then + if [ "$frontend" = "verific" -o "$frontend" = "verific_gates" ] && grep -q VERIFIC-SKIP ${bn}_ref.v; then touch ../${bn}.skip return fi if [ -n "$scriptfiles" ]; then - test_passes $fn $scriptfiles + test_passes ${bn}_ref.v $scriptfiles elif [ -n "$scriptopt" ]; then - test_passes -f "$frontend" -p "$scriptopt" $fn + test_passes -f "$frontend" -p "$scriptopt" ${bn}_ref.v elif [ "$frontend" = "verific" ]; then - test_passes -p "verific -vlog2k $fn; verific -import -all; opt; memory;;" + test_passes -p "verific -vlog2k ${bn}_ref.v; verific -import -all; opt; memory;;" elif [ "$frontend" = "verific_gates" ]; then - test_passes -p "verific -vlog2k $fn; verific -import -gates -all; opt; memory;;" + test_passes -p "verific -vlog2k ${bn}_ref.v; verific -import -gates -all; opt; memory;;" else - test_passes -f "$frontend" -p "hierarchy; proc; opt; memory; opt; fsm; opt -fine" $fn - test_passes -f "$frontend" -p "hierarchy; synth -run coarse; techmap; opt; abc -dff" $fn + test_passes -f "$frontend" -p "hierarchy; proc; opt; memory; opt; fsm; opt -full -fine" ${bn}_ref.v + test_passes -f "$frontend" -p "hierarchy; synth -run coarse; techmap; opt; abc -dff" ${bn}_ref.v fi touch ../${bn}.log } @@ -168,7 +164,7 @@ do else echo "${status_prefix}-> ERROR!" if $warn_iverilog_git; then - echo "Note: Make sure that 'iverilog' is an up-to-date git checkout of icarus verilog." + echo "Note: Make sure that 'iverilog' is an up-to-date git checkout of Icarus Verilog." fi $keeprunning || exit 1 fi diff --git a/tests/tools/txt2tikztiming.py b/tests/tools/txt2tikztiming.py index cfefe339f..9c6cd3a19 100755 --- a/tests/tools/txt2tikztiming.py +++ b/tests/tools/txt2tikztiming.py @@ -1,7 +1,4 @@ -#!/usr/bin/python - -from __future__ import division -from __future__ import print_function +#!/usr/bin/env python3 import argparse import fileinput diff --git a/tests/tools/vcdcd.pl b/tests/tools/vcdcd.pl index 6f497e99c..58a92b44d 100755 --- a/tests/tools/vcdcd.pl +++ b/tests/tools/vcdcd.pl @@ -35,7 +35,7 @@ if ($#ARGV != 1) { print STDERR "Usage: $0 [-w N] [-d N] gold.vcd gate.vcd\n"; print STDERR "\n"; print STDERR " -w N\n"; - print STDERR " reserve N characters for bitmap in text ouput (default: auto)\n"; + print STDERR " reserve N characters for bitmap in text output (default: auto)\n"; print STDERR "\n"; print STDERR " -d N\n"; print STDERR " allow for N timesteps delay between gate and gold (default: 0)\n"; diff --git a/tests/various/muxcover.ys b/tests/various/muxcover.ys new file mode 100644 index 000000000..7ac460f13 --- /dev/null +++ b/tests/various/muxcover.ys @@ -0,0 +1,51 @@ + +read_verilog -formal <<EOT + module gate (input [2:0] A, B, C, D, X, output reg [2:0] Y); + always @* + (* parallel_case *) + casez (X) + 3'b??1: Y = A; + 3'b?1?: Y = B; + 3'b1??: Y = C; + 3'b000: Y = D; + endcase + endmodule +EOT + + +## Examle usage for "pmuxtree" and "muxcover" + +proc +pmuxtree +techmap +muxcover -mux4 + +splitnets -ports +clean +# show + + +## Equivalence checking + +read_verilog -formal <<EOT + module gold (input [2:0] A, B, C, D, X, output reg [2:0] Y); + always @* + casez (X) + 3'b001: Y = A; + 3'b010: Y = B; + 3'b100: Y = C; + 3'b000: Y = D; + default: Y = 'bx; + endcase + endmodule +EOT + +proc +splitnets -ports +techmap -map +/simcells.v t:$_MUX4_ + +equiv_make gold gate equiv +hierarchy -top equiv +equiv_simple -undef +equiv_status -assert + diff --git a/tests/vloghtb/common.sh b/tests/vloghtb/common.sh index 3965b04ca..a8335c2bd 100644 --- a/tests/vloghtb/common.sh +++ b/tests/vloghtb/common.sh @@ -68,3 +68,39 @@ test_equiv() log_pass test_$1 $4 mv log_test_$1/$4.out log_test_$1/$4.txt } + +test_febe() +{ + # Usage: + # test_febe <test_name> <synth_script> <extension> <backend> <frontend> <sat_options> <mod_name> <vlog_file> + # $1 $2 $3 $4 $5 $6 $7 $8 + + mkdir -p log_test_$1 + rm -f log_test_$1/$7.txt + rm -f log_test_$1/$7.err + + if ! ../../yosys -q -l log_test_$1/$7.out - 2> /dev/null <<- EOT + echo on + read_verilog $8 + $2 + design -save gold + dump + $4 log_test_$1/$7$3 + design -reset + $5 log_test_$1/$7$3 + + design -copy-from gold -as gold $7 + rename $7 gate + + miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp gold gate miter + sat $6 -verify -prove trigger 0 -show-inputs -show-outputs miter + EOT + then + log_fail test_$1 $7 + mv log_test_$1/$7.out log_test_$1/$7.err + exit 1 + fi + + log_pass test_$1 $7 + mv log_test_$1/$7.out log_test_$1/$7.txt +} diff --git a/tests/vloghtb/test_febe.sh b/tests/vloghtb/test_febe.sh new file mode 100644 index 000000000..482d44d9a --- /dev/null +++ b/tests/vloghtb/test_febe.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e +source common.sh + +f=$1 +n=$(basename ${f%.v}) + +test_febe vlog1 "synth" ".v" "write_verilog" "read_verilog" "-ignore_div_by_zero" $n $f +test_febe vlog2 "synth -run coarse" ".v" "write_verilog" "read_verilog -icells" "-ignore_div_by_zero" $n $f +test_febe blif "synth; splitnets -ports" ".blif" "write_blif" "read_blif" "-ignore_div_by_zero" $n $f + +exit 0 |