diff --git a/.gitignore b/.gitignore index 803e1302..2b6f032e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,9 +11,17 @@ /qtcreator.creator.user /Makefile.conf /abc +/viz.js /yosys +/yosys.exe +/yosys.js /yosys-abc +/yosys-abc.exe /yosys-config /yosys-filterlib +/yosys-filterlib.exe /kernel/version_*.cc /share +/yosys-win32-mxebin-* +/yosys-win32-vcxsrc-* +/yosysjs-* diff --git a/CHANGELOG b/CHANGELOG index 35695729..3efbe109 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,15 +1,133 @@ -List of changes and major improvements between releases +List of major changes and improvements between releases ======================================================= -Yosys 0.3.0 .. Yoys 0.3.0+ --------------------------- +Yosys 0.4 .. Yosys 0.5 +---------------------- - ... TBD ... + * API changes + - Added log_warning() + - Added eval_select_args() and eval_select_op() + - Added cell->known(), cell->input(portname), cell->output(portname) + - Skip blackbox modules in design->selected_modules() + - Replaced std::map<> and std::set<> with dict<> and pool<> + - New SigSpec::extend() is what used to be SigSpec::extend_u0() + - Added YS_OVERRIDE, YS_FINAL, YS_ATTRIBUTE, YS_NORETURN + + * Cell library changes + - Added flip-flops with enable ($dffe etc.) + - Added $equiv cells for equivalence checking framework + + * Various + - Updated ABC to hg rev 61ad5f908c03 + - Added clock domain partitioning to ABC pass + - Improved plugin building (see "yosys-config --build") + - Added ENABLE_NDEBUG Makefile flag for high-performance builds + - Added "yosys -d", "yosys -L" and other driver improvements + - Added support for multi-bit (array) cell ports to "write_edif" + - Now printing most output to stdout, not stderr + - Added "onehot" attribute (set by "fsm_map") + - Various performance improvements + - Vastly improved Xilinx flow + - Added "make unsintall" + + * Equivalence checking + - Added equivalence checking commands: + equiv_make equiv_simple equiv_status + equiv_induct equiv_miter + equiv_add equiv_remove + + * Block RAM support: + - Added "memory_bram" command + - Added BRAM support to Xilinx flow + + * Other New Commands and Options + - Added "dff2dffe" + - Added "fsm -encfile" + - Added "dfflibmap -prepare" + - Added "write_blid -unbuf -undef -blackbox" + - Added "write_smt2" for writing SMT-LIBv2 files + - Added "test_cell -w -muxdiv" + - Added "select -read" -Yosys 0.2.0 .. Yoys 0.3.0 +Yosys 0.3.0 .. Yosys 0.4 +------------------------ + + * Platform Support + - Added support for mxe-based cross-builds for win32 + - Added sourcecode-export as VisualStudio project + - Added experimental EMCC (JavaScript) support + + * Verilog Frontend + - Added -sv option for SystemVerilog (and automatic *.sv file support) + - Added support for real-valued constants and constant expressions + - Added support for non-standard "via_celltype" attribute on task/func + - Added support for non-standard "module mod_name(...);" syntax + - Added support for non-standard """ macro bodies + - Added support for array with more than one dimension + - Added support for $readmemh and $readmemb + - Added support for DPI functions + + * Changes in internal cell library + - Added $shift and $shiftx cell types + - Added $alu, $lcu, $fa and $macc cell types + - Removed $bu0 and $safe_pmux cell types + - $mem/$memwr WR_EN input is now a per-data-bit enable signal + - Added $_NAND_ $_NOR_ $_XNOR_ $_AOI3_ $_OAI3_ $_AOI4_ $_OAI4_ + - Renamed ports of $lut cells (from I->O to A->Y) + - Renamed $_INV_ to $_NOT_ + + * 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 + - Various new high-level optimizations on RTL netlist + - Various improvements in FSM optimization + - Updated ABC to hg 5b5af75f1dda (from 2014-11-07) + + * Changes in internal APIs and RTLIL + - Added log_id() and log_cell() helper functions + - Added function-like cell creation helpers + - Added GetSize() function (like .size() but with int) + - Major refactoring of RTLIL::Module and related classes + - Major refactoring of RTLIL::SigSpec and related classes + - Now RTLIL::IdString is essentially an int + - 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 + - Added RTLIL::Monitor API and "trace" command + - Added "Yosys" C++ namespace + + * Changes relevant to SAT solving + - Added ezSAT::keep_cnf() and ezSAT::non_incremental() + - Added native ezSAT support for vector shift ops + - Updated MiniSAT to git 37dc6c67e2 (from 2013-09-25) + + * New commands (or large improvements to commands) + - Added "synth" command with default script + - Added "share" (finally some real resource sharing) + - Added "memory_share" (reduce number of ports on memories) + - Added "wreduce" and "alumacc" commands + - Added "opt -keepdc -fine -full -fast" + - Added some "test_*" commands + + * Various other changes + - Added %D and %c select operators + - Added support for labels in yosys scripts + - Added support for here-documents in yosys scripts + - Support "+/" prefix for files from proc_share_dir + - Added "autoidx" statement to ilang language + - Switched from "yosys-svgviewer" to "xdot" + - Renamed "stdcells.v" to "techmap.v" + - Various bug fixes and small improvements + - Improved welcome and bye messages + + +Yosys 0.2.0 .. Yosys 0.3.0 -------------------------- * Driver program and overall behavior: @@ -59,8 +177,8 @@ Yosys 0.2.0 .. Yoys 0.3.0 - Various build fixes for OSX (Darwin) and OpenBSD -Yosys 0.1.0 .. Yoys 0.2.0 -------------------------- +Yosys 0.1.0 .. Yosys 0.2.0 +-------------------------- * Changes to the driver program: - Added "yosys -h" and "yosys -H" diff --git a/CodingReadme b/CodingReadme index 8f515e1f..54ea368e 100644 --- a/CodingReadme +++ b/CodingReadme @@ -1,31 +1,209 @@ +This file contains some very brief documentation on things like programming APIs. +Also consult the Yosys manual and the section about programming in the presentation. +(Both can be downloaded as PDF from the yosys webpage.) + +--snip-- only the lines below this mark are included in the yosys manual --snip-- Getting Started =============== -Reading List ------------- +Outline of a Yosys command +-------------------------- -To write Yosys C++ code you need to know at least the following classes in kernel/rtlil.h: +Here is a the C++ code for a "hello_world" Yosys command (hello.cc): + + #include "kernel/yosys.h" + + USING_YOSYS_NAMESPACE + PRIVATE_NAMESPACE_BEGIN + + struct HelloWorldPass : public Pass { + HelloWorldPass() : Pass("hello_world") { } + virtual void execute(vector, Design*) { + log("Hello World!\n"); + } + } HelloWorldPass; + + PRIVATE_NAMESPACE_END + +This can be built into a Yosys module using the following command: + + yosys-config --exec --cxx --cxxflags --ldflags -o hello.so -shared hello.cc --ldlibs + +Or short: + + yosys-config --build hello.so hello.cc + +And then executed using the following command: + + yosys -m hello.so -p hello_world + + +Yosys Data Structures +--------------------- + +Here is a short list of data structures that you should make yourself familiar +with before you write C++ code for Yosys. The following data structures are all +defined when "kernel/yosys.h" is included and USING_YOSYS_NAMESPACE is used. + + 1. Yosys Container Classes + +Yosys uses dict and pool as main container classes. dict is +essentially a replacement for std::unordered_map and pool is a +replacement for std::unordered_set. The main characteristics are: + + - dict and pool are about 2x faster than the std containers + + - references to elements in a dict or pool are invalidated by + insert and remove operations (similar to std::vector on push_back()). + + - some iterators are invalidated by erase(). specifically, iterators + that have not passed the erased element yet are invalidated. (erase() + itself returns valid iterator to the next element.) + + - no iterators are invalidated by insert(). elements are inserted at + begin(). i.e. only a new iterator that starts at begin() will see the + inserted elements. + + - the method .count(key, iterator) is like .count(key) but only + considers elements that can be reached via the iterator. + + - iterators can be compared. it1 < it2 means that the position of t2 + can be reached via t1 but not vice versa. + + - the method .sort() can be used to sort the elements in the container + the container stays sorted until elements are added or removed. + + - dict and pool will have the same order of iteration across + all compilers, standard libraries and architectures. + +In addition to dict and pool there is also an idict that +creates a bijective map from K to the integers. For example: + + idict si; + log("%d\n", si("hello")); // will print 42 + log("%d\n", si("world")); // will print 43 + log("%d\n", si.at("world")); // will print 43 + log("%d\n", si.at("dummy")); // will throw exception + log("%s\n", si[42].c_str())); // will print hello + log("%s\n", si[43].c_str())); // will print world + log("%s\n", si[44].c_str())); // will throw exception + +It is not possible to remove elements from an idict. + + 2. Standard STL data types + +In Yosys we use std::vector and std::string whenever applicable. When +dict and pool are not suitable then std::map and std::set +are used instead. + +The types std::vector and std::string are also available as vector +and string in the Yosys namespace. + + 3. RTLIL objects + +The current design (essentially a collection of modules, each defined by a +netlist) is stored in memory using RTLIL object (declared in kernel/rtlil.h, +automatically included by kernel/yosys.h). You should glance over at least +the declarations for the following types in kernel/rtlil.h: + + RTLIL::IdString + This is a handle for an identifier (e.g. cell or wire name). + It feels a lot like a std::string, but is only a single int + in size. (The actual string is stored in a global lookup + table.) + + RTLIL::SigBit + A single signal bit. I.e. either a constant state (0, 1, + x, z) or a single bit from a wire. + + RTLIL::SigSpec + Essentially a vector of SigBits. RTLIL::Wire RTLIL::Cell + The building blocks of the netlist in a module. + RTLIL::Module - RTLIL::SigSpec + RTLIL::Design + The module is a container with connected cells and wires + in it. The design is a container with modules in it. + +All this types are also available without the RTLIL:: prefix in the Yosys +namespace. + + 4. SigMap and other Helper Classes + +There are a couple of additional helper classes that are in wide use +in Yosys. Most importantly there is SigMap (declared in kernel/sigtools.h). + +When a design has many wires in it that are connected to each other, then a +single signal bit can have multiple valid names. The SigMap object can be used +to map SigSpecs or SigBits to unique SigSpecs and SigBits that consistently +only use one wire from such a group of connected wires. For example: + + SigBit a = module->addWire(NEW_ID); + SigBit b = module->addWire(NEW_ID); + module->connect(a, b); + + log("%d\n", a == b); // will print 0 + + SigMap sigmap(module); + 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_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 +------------ The following yosys commands are a good starting point if you are looking for examples of how to use the Yosys API: - passes/opt/wreduce.cc - passes/techmap/maccmap.cc + manual/CHAPTER_Prog/stubnets.cc + manual/PRESENTATION_Prog/my_cmd.cc Notes on the existing codebase ------------------------------ For historical reasons not all parts of Yosys adhere to the current coding -styles. When adding code to existing parts of the system, adhere to this guide +style. When adding code to existing parts of the system, adhere to this guide for the new code instead of trying to mimic the style of the surrounding code. @@ -58,15 +236,58 @@ C++ Langugage ------------- Yosys is written in C++11. At the moment only constructs supported by -gcc 4.6 is allowed in Yosys code. This will change in future releases. +gcc 4.6 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 "SIZE(foobar)" instead -of "foobar.size()". (the macro SIZE() is defined by kernel/yosys.h) +warnings for implicit type casts, always use "GetSize(foobar)" instead +of "foobar.size()". (GetSize() is defined in kernel/yosys.h) Use range-based for loops whenever applicable. +--snap-- only the lines above this mark are included in the yosys manual --snap-- + + +Creating the Visual Studio Template Project +=========================================== + +1. Create an empty Visual C++ Win32 Console App project + + Microsoft Visual Studio Express 2013 for Windows Desktop + Open New Project Wizard (File -> New Project..) + + Project Name: YosysVS + Solution Name: YosysVS + [X] Create directory for solution + [ ] Add to source control + + [X] Console applications + [X] Empty Projcect + [ ] SDL checks + +2. Open YosysVS Project Properties + + Select Configuration: All Configurations + + C/C++ -> General -> Additional Include Directories + Add: ..\yosys + + C/C++ -> Preprocessor -> Preprocessor Definitions + Add: _YOSYS_;_CRT_SECURE_NO_WARNINGS + +3. Resulting file system tree: + + YosysVS/ + YosysVS/YosysVS + YosysVS/YosysVS/YosysVS.vcxproj + YosysVS/YosysVS/YosysVS.vcxproj.filters + YosysVS/YosysVS.sdf + YosysVS/YosysVS.sln + YosysVS/YosysVS.v12.suo + +4. Zip YosysVS as YosysVS-Tpl-v1.zip + + Checklist for adding internal cell types ======================================== @@ -96,50 +317,45 @@ Update the CHANGELOG file: vi CHANGELOG -Run all tests with "make config-{clang-debug,gcc-debug,gcc-4.6,release}": - - cd ~yosys - make clean - make test vloghtb - make install - - cd ~yosys-bigsim - make clean - make full - - cd ~vloghammer - make purge - make gen_issues gen_samples - make SYN_LIST="yosys" SIM_LIST="icarus yosim verilator" FULL=1 world - chromium-browser report.html - - -Then with default config setting: - - cd ~yosys - ./yosys -p 'proc; show' tests/simple/fiedler-cooley.v - ./yosys -p 'proc; opt; show' tests/simple/fiedler-cooley.v +Update and check documentation: cd ~yosys + 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 ~yosys + vi README CodingReadme + - is the information provided in those file still up to date -Also with default config setting: + +Then with default config setting: + + cd ~yosys + make vgtest + + cd ~yosys + ./yosys -p 'proc; show' tests/simple/fiedler-cooley.v + ./yosys -p 'proc; opt; show' tests/simple/fiedler-cooley.v + ./yosys -p 'synth; show' tests/simple/fiedler-cooley.v + ./yosys -p 'synth_xilinx -top up3down5; show' tests/simple/fiedler-cooley.v cd ~yosys/techlibs/cmos bash testbench.sh - cd ~yosys/techlibs/xilinx/example_sim_counter - bash run_sim.sh - - cd ~yosys/techlibs/xilinx/example_mojo_counter - bash example.sh + cd ~yosys/techlibs/xilinx/example_basys3 + bash run.sh -Finally if a current verific library is available: +Test building plugins with various of the standard passes: + + yosys-config --build test.so equiv_simple.cc + - also check the code examples in CodingReadme + + +And if a version of the verific library is currently available: cd ~yosys cat frontends/verific/build_amd64.txt @@ -149,12 +365,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.6}": - - 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: @@ -166,7 +392,6 @@ Release: - push tag to github - post changelog on github - post short release note on reddit - - delete -rc branch from github Updating the website: @@ -183,12 +408,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+" - - diff --git a/Makefile b/Makefile index 5a75320c..405870b6 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ CONFIG := clang # CONFIG := gcc # CONFIG := gcc-4.6 # CONFIG := emcc +# CONFIG := mxe # features (the more the better) ENABLE_TCL := 1 @@ -10,41 +11,53 @@ ENABLE_ABC := 1 ENABLE_PLUGINS := 1 ENABLE_READLINE := 1 ENABLE_VERIFIC := 0 +ENABLE_COVER := 1 # other configuration flags ENABLE_GPROF := 0 +ENABLE_NDEBUG := 0 DESTDIR := /usr/local INSTALL_SUDO := +TARGET_BINDIR := $(DESTDIR)/bin +TARGET_DATDIR := $(DESTDIR)/share/yosys + +EXE = OBJS = GENFILES = +EXTRA_OBJS = EXTRA_TARGETS = -TARGETS = yosys yosys-config +TARGETS = yosys$(EXE) yosys-config PRETTY = 1 SMALL = 0 all: top-all -CXXFLAGS = -Wall -Wextra -ggdb -I"$(shell pwd)" -MD -DYOSYS_SRC='"$(shell pwd)"' -D_YOSYS_ -fPIC -I${DESTDIR}/include -LDFLAGS = -L${DESTDIR}/lib +YOSYS_SRC := $(shell pwd) +CXXFLAGS = -Wall -Wextra -ggdb -I"$(YOSYS_SRC)" -MD -D_YOSYS_ -fPIC -I$(DESTDIR)/include +LDFLAGS = -L$(DESTDIR)/lib LDLIBS = -lstdc++ -lm -QMAKE = qmake-qt4 SED = sed +BISON = bison ifeq (Darwin,$(findstring Darwin,$(shell uname))) - # add macports include and library path to search directories, don't use '-rdynamic' and '-lrt': - CXXFLAGS += -I/opt/local/include - LDFLAGS += -L/opt/local/lib - QMAKE = qmake + # add macports/homebrew include and library path to search directories, don't use '-rdynamic' and '-lrt': + CXXFLAGS += -I/opt/local/include -I/usr/local/opt/readline/include + LDFLAGS += -L/opt/local/lib -L/usr/local/opt/readline/lib + # add homebrew's libffi include and library path + CXXFLAGS += $(shell PKG_CONFIG_PATH=$$(brew list libffi | grep pkgconfig | xargs dirname) pkg-config --silence-errors --cflags libffi) + 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 else LDFLAGS += -rdynamic LDLIBS += -lrt endif -YOSYS_VER := 0.3.0+$(shell test -d .git && { git log --author=clifford@clifford.at --oneline ca125bf41.. | wc -l; }) +YOSYS_VER := 0.5+$(shell test -d .git && { git log --author=clifford@clifford.at --oneline c3c9fbfb8c678.. | wc -l; }) GIT_REV := $(shell git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN) OBJS = kernel/version_$(GIT_REV).o @@ -54,8 +67,9 @@ 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 = 4d547a5e065b +ABCREV = 61ad5f908c03 ABCPULL = 1 +ABCMKARGS = # CC="$(CXX)" CXX="$(CXX)" define newline @@ -81,8 +95,42 @@ CXXFLAGS += -std=gnu++0x -Os else ifeq ($(CONFIG),emcc) CXX = emcc -CXXFLAGS += -std=c++11 -Os -Wno-warn-absolute-paths -CXXFLAGS := $(filter-out -ggdb,$(CXXFLAGS)) +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 DISABLE_EXCEPTION_CATCHING=0 -s ALLOW_MEMORY_GROWTH=1 +# 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 +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)" +EXE = .exe else ifneq ($(CONFIG),none) $(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, gcc-4.6, emcc, none) @@ -91,6 +139,9 @@ endif ifeq ($(ENABLE_READLINE),1) CXXFLAGS += -DYOSYS_ENABLE_READLINE LDLIBS += -lreadline +ifeq ($(CONFIG),mxe) +LDLIBS += -lpdcurses +endif endif ifeq ($(ENABLE_PLUGINS),1) @@ -99,7 +150,7 @@ LDLIBS += $(shell pkg-config --silence-errors --libs libffi || echo -lffi) -ldl endif ifeq ($(ENABLE_TCL),1) -TCL_VERSION ?= tcl8.5 +TCL_VERSION ?= tcl$(shell echo 'puts [info tclversion]' | tclsh) TCL_INCLUDE ?= /usr/include/$(TCL_VERSION) CXXFLAGS += -I$(TCL_INCLUDE) -DYOSYS_ENABLE_TCL LDLIBS += -l$(TCL_VERSION) @@ -110,9 +161,13 @@ CXXFLAGS += -pg LDFLAGS += -pg endif +ifeq ($(ENABLE_NDEBUG),1) +CXXFLAGS := -O3 -DNDEBUG $(filter-out -Os,$(CXXFLAGS)) +endif + ifeq ($(ENABLE_ABC),1) CXXFLAGS += -DYOSYS_ENABLE_ABC -TARGETS += yosys-abc +TARGETS += yosys-abc$(EXE) endif ifeq ($(ENABLE_VERIFIC),1) @@ -122,11 +177,26 @@ CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABL LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) endif +ifeq ($(ENABLE_COVER),1) +CXXFLAGS += -DYOSYS_ENABLE_COVER +endif + +define add_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 + +define add_include_file +$(eval $(call add_share_file,$(dir share/include/$(1)),$(1))) +endef + ifeq ($(PRETTY), 1) P_STATUS = 0 P_OFFSET = 0 -P_UPDATE = $(eval P_STATUS=$(shell echo $(OBJS) yosys | gawk 'BEGIN { RS = " "; I = $(P_STATUS)+0; } $$1 == "$@" && NR > I { I = NR; } END { print I; }')) -P_SHOW = [$(shell gawk "BEGIN { N=$(words $(OBJS) yosys); printf \"%3d\", $(P_OFFSET)+90*$(P_STATUS)/N; exit; }")%] +P_UPDATE = $(eval P_STATUS=$(shell echo $(OBJS) yosys$(EXE) | gawk 'BEGIN { RS = " "; I = $(P_STATUS)+0; } $$1 == "$@" && NR > I { I = NR; } END { print I; }')) +P_SHOW = [$(shell gawk "BEGIN { N=$(words $(OBJS) yosys$(EXE)); printf \"%3d\", $(P_OFFSET)+90*$(P_STATUS)/N; exit; }")%] P = @echo "$(if $(findstring $@,$(TARGETS) $(EXTRA_TARGETS)),$(eval P_OFFSET = 10))$(call P_UPDATE)$(call P_SHOW) Building $@"; Q = @ S = -s @@ -137,7 +207,26 @@ Q = S = endif +$(eval $(call add_include_file,kernel/yosys.h)) +$(eval $(call add_include_file,kernel/hashlib.h)) +$(eval $(call add_include_file,kernel/log.h)) +$(eval $(call add_include_file,kernel/rtlil.h)) +$(eval $(call add_include_file,kernel/register.h)) +$(eval $(call add_include_file,kernel/celltypes.h)) +$(eval $(call add_include_file,kernel/consteval.h)) +$(eval $(call add_include_file,kernel/sigtools.h)) +$(eval $(call add_include_file,kernel/modtools.h)) +$(eval $(call add_include_file,kernel/macc.h)) +$(eval $(call add_include_file,kernel/utils.h)) +$(eval $(call add_include_file,kernel/satgen.h)) +$(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,backends/ilang/ilang_backend.h)) + OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o +kernel/log.o: CXXFLAGS += -DYOSYS_SRC='"$(YOSYS_SRC)"' OBJS += libs/bigint/BigIntegerAlgorithms.o libs/bigint/BigInteger.o libs/bigint/BigIntegerUtils.o OBJS += libs/bigint/BigUnsigned.o libs/bigint/BigUnsignedInABase.o @@ -192,8 +281,8 @@ top-all: $(TARGETS) $(EXTRA_TARGETS) @echo " Build successful." @echo "" -yosys: $(OBJS) - $(P) $(CXX) -o yosys $(LDFLAGS) $(OBJS) $(LDLIBS) +yosys$(EXE): $(OBJS) + $(P) $(CXX) -o yosys$(EXE) $(LDFLAGS) $(OBJS) $(LDLIBS) %.o: %.cc $(P) $(CXX) -o $@ -c $(CXXFLAGS) $< @@ -203,15 +292,16 @@ yosys: $(OBJS) kernel/version_$(GIT_REV).cc: Makefile $(P) rm -f kernel/version_*.o kernel/version_*.d kernel/version_*.cc - $(Q) echo "extern const char *yosys_version_str; const char *yosys_version_str=\"Yosys $(YOSYS_VER) (git sha1 $(GIT_REV), $(CXX) ` \ - $(CXX) --version | tr ' ()' '\n' | grep '^[0-9]' | head -n1` $(filter -f% -m% -O% -DNDEBUG,$(CXXFLAGS)))\";" > kernel/version_$(GIT_REV).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)) ` \ + $(CXX) --version | tr ' ()' '\n' | grep '^[0-9]' | head -n1` $(filter -f% -m% -O% -DNDEBUG,$(CXXFLAGS)))\"; }" > kernel/version_$(GIT_REV).cc -yosys-config: yosys-config.in - $(P) $(SED) -e 's,@CXX@,$(CXX),;' -e 's,@CXXFLAGS@,$(CXXFLAGS),;' -e 's,@LDFLAGS@,$(LDFLAGS),;' -e 's,@LDLIBS@,$(LDLIBS),;' \ - -e 's,@BINDIR@,$(DESTDIR)/bin,;' -e 's,@DATDIR@,$(DESTDIR)/share/yosys,;' < yosys-config.in > yosys-config +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 $(Q) chmod +x yosys-config -abc/abc-$(ABCREV): +abc/abc-$(ABCREV)$(EXE): $(P) ifneq ($(ABCREV),default) $(Q) if ( cd abc 2> /dev/null && hg identify; ) | grep -q +; then \ @@ -219,20 +309,20 @@ ifneq ($(ABCREV),default) fi $(Q) if test "`cd abc 2> /dev/null && hg identify | cut -f1 -d' '`" != "$(ABCREV)"; then \ test $(ABCPULL) -ne 0 || { echo 'REEBE: NOP abg hc gb qngr naq NOPCHYY frg gb 0 va Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; exit 1; }; \ - echo "Pulling ABC from bitbucket.org:"; \ + echo "Pulling ABC from bitbucket.org:"; set -x; \ test -d abc || hg clone https://bitbucket.org/alanmi/abc abc; \ - cd abc && hg pull && hg update -r $(ABCREV); \ + cd abc && $(MAKE) DEP= clean && hg pull && hg update -r $(ABCREV); \ fi endif $(Q) rm -f abc/abc-[0-9a-f]* - $(Q) cd abc && $(MAKE) $(S) PROG="abc-$(ABCREV)" MSG_PREFIX="$(eval P_OFFSET = 5)$(call P_SHOW)$(eval P_OFFSET = 10) ABC: " + $(Q) cd abc && $(MAKE) $(S) $(ABCMKARGS) PROG="abc-$(ABCREV)$(EXE)" MSG_PREFIX="$(eval P_OFFSET = 5)$(call P_SHOW)$(eval P_OFFSET = 10) ABC: " ifeq ($(ABCREV),default) -.PHONY: abc/abc-$(ABCREV) +.PHONY: abc/abc-$(ABCREV)$(EXE) endif -yosys-abc: abc/abc-$(ABCREV) - $(P) cp abc/abc-$(ABCREV) yosys-abc +yosys-abc$(EXE): abc/abc-$(ABCREV)$(EXE) + $(P) cp abc/abc-$(ABCREV)$(EXE) yosys-abc$(EXE) test: $(TARGETS) $(EXTRA_TARGETS) +cd tests/simple && bash run-test.sh @@ -243,6 +333,7 @@ test: $(TARGETS) $(EXTRA_TARGETS) +cd tests/fsm && bash run-test.sh +cd tests/techmap && bash run-test.sh +cd tests/memories && bash run-test.sh + +cd tests/bram && bash run-test.sh +cd tests/various && bash run-test.sh +cd tests/sat && bash run-test.sh @echo "" @@ -252,7 +343,7 @@ test: $(TARGETS) $(EXTRA_TARGETS) VALGRIND ?= valgrind --error-exitcode=1 --leak-check=full --show-reachable=yes --errors-for-leak-kinds=all vgtest: $(TARGETS) $(EXTRA_TARGETS) - $(VALGRIND) ./yosys -p 'setattr -mod -unset top; hierarchy; proc; opt; memory -nomap; opt -fine; techmap; opt' $$( ls tests/simple/*.v | grep -v repwhile.v ) + $(VALGRIND) ./yosys -p 'setattr -mod -unset top; synth' $$( ls tests/simple/*.v | grep -v repwhile.v ) @echo "" @echo " Passed \"make vgtest\"." @echo "" @@ -269,6 +360,13 @@ install: $(TARGETS) $(EXTRA_TARGETS) $(INSTALL_SUDO) mkdir -p $(DESTDIR)/share/yosys $(INSTALL_SUDO) cp -r share/. $(DESTDIR)/share/yosys/. +uninstall: + $(INSTALL_SUDO) rm -vf $(addprefix $(DESTDIR)/bin/,$(notdir $(TARGETS))) + $(INSTALL_SUDO) rm -rvf $(DESTDIR)/share/yosys/ + +update-manual: $(TARGETS) $(EXTRA_TARGETS) + cd manual && ../yosys -p 'help -write-tex-command-reference-manual' + manual: $(TARGETS) $(EXTRA_TARGETS) cd manual && bash appnotes.sh cd manual && bash presentation.sh @@ -277,13 +375,13 @@ manual: $(TARGETS) $(EXTRA_TARGETS) clean: rm -rf share cd manual && bash clean.sh - rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) + 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 clean-abc: - make -C abc clean - rm -f yosys-abc abc/abc-[0-9a-f]* + $(MAKE) -C abc DEP= clean + rm -f yosys-abc$(EXE) abc/abc-[0-9a-f]* mrproper: clean git clean -xdf @@ -295,6 +393,29 @@ qtcreator: { echo .; find backends frontends kernel libs passes -type f \( -name '*.h' -o -name '*.hh' \) -printf '%h\n' | sort -u; } > qtcreator.includes touch qtcreator.config qtcreator.creator +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 + 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 + zip -r yosys-win32-vcxsrc-$(YOSYS_VER).zip yosys-win32-vcxsrc-$(YOSYS_VER)/ + rm -f srcfiles.txt kernel/version.cc + +ifeq ($(CONFIG),mxe) +mxebin: $(TARGETS) $(EXTRA_TARGETS) + rm -rf yosys-win32-mxebin-$(YOSYS_VER){,.zip} + mkdir -p yosys-win32-mxebin-$(YOSYS_VER) + cp -r yosys.exe share/ yosys-win32-mxebin-$(YOSYS_VER)/ +ifeq ($(ENABLE_ABC),1) + cp -r yosys-abc.exe abc/lib/x86/pthreadVC2.dll yosys-win32-mxebin-$(YOSYS_VER)/ +endif + echo -en 'This is Yosys $(YOSYS_VER) for Win32.\r\n' > yosys-win32-mxebin-$(YOSYS_VER)/readme.txt + echo -en 'Documentation at http://www.clifford.at/yosys/.\r\n' >> yosys-win32-mxebin-$(YOSYS_VER)/readme.txt + zip -r yosys-win32-mxebin-$(YOSYS_VER).zip yosys-win32-mxebin-$(YOSYS_VER)/ +endif + config-clean: clean rm -f Makefile.conf @@ -314,6 +435,12 @@ config-emcc: clean echo 'ENABLE_PLUGINS := 0' >> Makefile.conf echo 'ENABLE_READLINE := 0' >> Makefile.conf +config-mxe: clean + echo 'CONFIG := mxe' > Makefile.conf + echo 'ENABLE_TCL := 0' >> Makefile.conf + echo 'ENABLE_PLUGINS := 0' >> Makefile.conf + echo 'ENABLE_READLINE := 0' >> Makefile.conf + config-gprof: clean echo 'CONFIG := gcc' > Makefile.conf echo 'ENABLE_GPROF := 1' >> Makefile.conf @@ -321,6 +448,12 @@ config-gprof: clean config-sudo: echo "INSTALL_SUDO := sudo" >> Makefile.conf +echo-yosys-ver: + @echo "$(YOSYS_VER)" + +echo-git-rev: + @echo "$(GIT_REV)" + -include libs/*/*.d -include frontends/*/*.d -include passes/*/*.d diff --git a/README b/README index d7f5aaa4..856f6079 100644 --- a/README +++ b/README @@ -3,7 +3,7 @@ | | | yosys -- Yosys Open SYnthesis Suite | | | - | Copyright (C) 2012 Clifford Wolf | + | Copyright (C) 2012 - 2015 Clifford Wolf | | | | Permission to use, copy, modify, and/or distribute this software for any | | purpose with or without fee is hereby granted, provided that the above | @@ -55,12 +55,13 @@ Xdot (graphviz) is used by the "show" command in yosys to display schematics. 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 - tcl8.5-dev libffi-dev git mercurial graphviz xdot" + $ yosys_deps="build-essential clang bison flex libreadline-dev gawk + tcl-dev libffi-dev git mercurial graphviz xdot pkg-config" $ sudo apt-get install $yosys_deps -There are also pre-compiled packages for Yosys on Ubuntu. Visit the Yosys -download page to learn more about this: +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 @@ -248,11 +249,19 @@ Verilog Attributes and non-standard features is strongly recommended instead). - The "nomem2reg" attribute on modules or arrays prohibits the - automatic early conversion of arrays to separate registers. + automatic early conversion of arrays to separate registers. 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. - 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 @@ -264,6 +273,9 @@ Verilog Attributes and non-standard features temporary variable within an always block. This is mostly used internally 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. + - 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 @@ -273,6 +285,11 @@ Verilog Attributes and non-standard features - The "keep" attribute on cells and wires is used to mark objects that should never be removed by the optimizer. This is used for example for cells that have hidden connections that are not part of the netlist, such as IO pads. + 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 @@ -289,7 +306,7 @@ Verilog Attributes and non-standard features by adding an empty {* *} statement.) - Modules can be declared with "module mod_name(...);" (with three dots - instead of a list of moudle ports). With this syntax it is sufficient + 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. @@ -354,40 +371,8 @@ from SystemVerilog: - The "assert" statement from SystemVerilog is supported in its most basic form. In module context: "assert property ();" and within an - always block: "assert();". It is transformed to a $assert cell - that is supported by the "sat" and "write_btor" commands. + always block: "assert();". It is transformed to a $assert cell. - The keywords "always_comb", "always_ff" and "always_latch", "logic" and "bit" are supported. - -Roadmap / Large-scale TODOs -=========================== - -- Technology mapping for real-world applications - - Improve Xilinx FGPA synthesis (RAMB, CARRY4, SLR, etc.) - -- Implement SAT-based formal equivialence checker - - Write equiv pass based on hint-based register mapping - -- Re-implement Verilog frontend (far future) - - cleaner (easier to use, harder to use wrong) AST format - - pipeline of well structured AST transformations - - true contextual name lookup - - -Other Unsorted TODOs -==================== - -- Implement missing Verilog 2005 features: - - - Support for real (float) const. expressions and parameters - - ROM modeling using $readmemh/$readmemb in "initial" blocks - - Ignore what needs to be ignored (e.g. drive and charge strengths) - - Check standard vs. implementation to identify missing features - -- Miscellaneous TODO items: - - - Add brief source code documentation to most passes and kernel code - - Implement mux-to-tribuf pass and rebalance mixed mux/tribuf trees - diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc index ee12546c..f6aac0b4 100644 --- a/backends/blif/blif.cc +++ b/backends/blif/blif.cc @@ -28,6 +28,9 @@ #include "kernel/log.h" #include +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct BlifDumperConfig { bool icells_mode; @@ -35,11 +38,14 @@ struct BlifDumperConfig bool impltf_mode; bool gates_mode; bool param_mode; + bool attr_mode; + bool blackbox_mode; std::string buf_type, buf_in, buf_out; - std::string true_type, true_out, false_type, false_out; + std::map> 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) { } + BlifDumperConfig() : icells_mode(false), conn_mode(false), impltf_mode(false), gates_mode(false), param_mode(false), attr_mode(false), blackbox_mode(false) { } }; struct BlifDumper @@ -69,8 +75,11 @@ struct BlifDumper const char *cstr(RTLIL::SigBit sig) { - if (sig.wire == NULL) - return sig == RTLIL::State::S1 ? "$true" : "$false"; + 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"; + return config->undef_type == "-" ? config->undef_out.c_str() : "$undef"; + } std::string str = RTLIL::unescape_id(sig.wire->name); for (size_t i = 0; i < str.size(); i++) @@ -95,6 +104,26 @@ struct BlifDumper return "subckt"; } + void dump_params(const char *command, dict ¶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"); @@ -126,23 +155,44 @@ struct BlifDumper } f << stringf("\n"); + if (module->get_bool_attribute("\\blackbox")) { + f << stringf(".blackbox\n"); + f << stringf(".end\n"); + return; + } + if (!config->impltf_mode) { - if (!config->false_type.empty()) - f << stringf(".%s %s %s=$false\n", subckt_or_gate(config->false_type), - config->false_type.c_str(), config->false_out.c_str()); - else + if (!config->false_type.empty()) { + if (config->false_type != "-") + f << stringf(".%s %s %s=$false\n", subckt_or_gate(config->false_type), + config->false_type.c_str(), config->false_out.c_str()); + } else f << stringf(".names $false\n"); - if (!config->true_type.empty()) - f << stringf(".%s %s %s=$true\n", subckt_or_gate(config->true_type), - config->true_type.c_str(), config->true_out.c_str()); - else + if (!config->true_type.empty()) { + if (config->true_type != "-") + f << stringf(".%s %s %s=$true\n", subckt_or_gate(config->true_type), + config->true_type.c_str(), config->true_out.c_str()); + } else f << stringf(".names $true\n1\n"); + if (!config->undef_type.empty()) { + if (config->undef_type != "-") + f << stringf(".%s %s %s=$undef\n", subckt_or_gate(config->undef_type), + config->undef_type.c_str(), config->undef_out.c_str()); + } else + f << stringf(".names $undef\n"); } for (auto &cell_it : module->cells_) { RTLIL::Cell *cell = cell_it.second; + if (config->unbuf_types.count(cell->type)) { + auto portnames = config->unbuf_types.at(cell->type); + f << stringf(".names %s %s\n1 1\n", + cstr(cell->getPort(portnames.first)), cstr(cell->getPort(portnames.second))); + continue; + } + if (!config->icells_mode && cell->type == "$_NOT_") { f << stringf(".names %s %s\n0 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\Y"))); @@ -191,21 +241,21 @@ struct BlifDumper auto &inputs = cell->getPort("\\A"); auto width = cell->parameters.at("\\WIDTH").as_int(); log_assert(inputs.size() == width); - for (int i = 0; i < inputs.size(); 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)); f << stringf("\n"); - auto mask = cell->parameters.at("\\LUT").as_string(); - for (int i = 0; i < (1 << width); i++) { - if (mask[i] == '0') continue; - for (int j = width-1; j >= 0; j--) { - f << ((i>>j)&1 ? '1' : '0'); + RTLIL::SigSpec mask = cell->parameters.at("\\LUT"); + for (int i = 0; i < (1 << width); i++) + if (mask[i] == RTLIL::S1) { + for (int j = width-1; j >= 0; j--) { + f << ((i>>j)&1 ? '1' : '0'); + } + f << " 1\n"; } - f << stringf(" %c\n", mask[i]); - } continue; } @@ -220,23 +270,10 @@ struct BlifDumper } f << stringf("\n"); + 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()) @@ -276,9 +313,17 @@ struct BlifBackend : public Backend { log(" -buf \n"); log(" use cells of type with the specified port names for buffers\n"); log("\n"); + log(" -unbuf \n"); + log(" replace buffer cells with the specified name and port names with\n"); + log(" a .names statement that models a buffer\n"); + log("\n"); log(" -true \n"); log(" -false \n"); - log(" use the specified cell types to drive nets that are constant 1 or 0\n"); + log(" -undef \n"); + log(" use the specified cell types to drive nets that are constant 1, 0, or\n"); + log(" undefined. when '-' is used as , then specifies\n"); + 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("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"); @@ -296,11 +341,17 @@ 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(" -blackbox\n"); + log(" write blackbox cells with .blackbox statement.\n"); log("\n"); log(" -impltf\n"); - log(" do not write definitions for the $true and $false wires.\n"); + log(" do not write definitions for the $true, $false and $undef wires.\n"); log("\n"); } virtual void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) @@ -326,6 +377,13 @@ struct BlifBackend : public Backend { config.buf_out = args[++argidx]; continue; } + if (args[argidx] == "-unbuf" && argidx+3 < args.size()) { + RTLIL::IdString unbuf_type = RTLIL::escape_id(args[++argidx]); + RTLIL::IdString unbuf_in = RTLIL::escape_id(args[++argidx]); + RTLIL::IdString unbuf_out = RTLIL::escape_id(args[++argidx]); + config.unbuf_types[unbuf_type] = std::pair(unbuf_in, unbuf_out); + continue; + } if (args[argidx] == "-true" && argidx+2 < args.size()) { config.true_type = args[++argidx]; config.true_out = args[++argidx]; @@ -336,6 +394,11 @@ struct BlifBackend : public Backend { config.false_out = args[++argidx]; continue; } + if (args[argidx] == "-undef" && argidx+2 < args.size()) { + config.undef_type = args[++argidx]; + config.undef_out = args[++argidx]; + continue; + } if (args[argidx] == "-icells") { config.icells_mode = true; continue; @@ -352,6 +415,14 @@ struct BlifBackend : public Backend { config.param_mode = true; continue; } + if (args[argidx] == "-attr") { + config.attr_mode = true; + continue; + } + if (args[argidx] == "-blackbox") { + config.blackbox_mode = true; + continue; + } if (args[argidx] == "-impltf") { config.impltf_mode = true; continue; @@ -372,7 +443,7 @@ struct BlifBackend : public Backend { for (auto module_it : design->modules_) { RTLIL::Module *module = module_it.second; - if (module->get_bool_attribute("\\blackbox")) + if (module->get_bool_attribute("\\blackbox") && !config.blackbox_mode) continue; if (module->processes.size() != 0) @@ -397,3 +468,4 @@ struct BlifBackend : public Backend { } } BlifBackend; +PRIVATE_NAMESPACE_END diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index 8ce5bb5e..c8fbf8d6 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -30,6 +30,9 @@ #include #include +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct BtorDumperConfig { bool subckt_mode; @@ -476,7 +479,7 @@ struct BtorDumper log_assert(!(cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex" || cell->type == "$ge" || cell->type == "$gt") || output_width == 1); bool l1_signed = cell->parameters.at(RTLIL::IdString("\\A_SIGNED")).as_bool(); - bool l2_signed = cell->parameters.at(RTLIL::IdString("\\B_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(); @@ -817,7 +820,7 @@ struct BtorDumper int input_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int(); log_assert(input->size() == input_width); int input_line = dump_sigspec(input, input_width); - const RTLIL::SigSpec* output = &cell->getPort(RTLIL::IdString("\\Y")); + 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(); @@ -1057,3 +1060,4 @@ struct BtorBackend : public Backend { } } BtorBackend; +PRIVATE_NAMESPACE_END diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc index ccedd91d..b089be14 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -27,6 +27,9 @@ #include "kernel/log.h" #include +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + #define EDIF_DEF(_id) edif_names(RTLIL::unescape_id(_id), true).c_str() #define EDIF_REF(_id) edif_names(RTLIL::unescape_id(_id), false).c_str() @@ -108,7 +111,7 @@ struct EdifBackend : public Backend { log_header("Executing EDIF backend.\n"); std::string top_module_name; - std::map> lib_cell_ports; + std::map> lib_cell_ports; CellTypes ct(design); EdifNames edif_names; @@ -147,12 +150,8 @@ struct EdifBackend : public Backend { RTLIL::Cell *cell = cell_it.second; if (!design->modules_.count(cell->type) || design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) { lib_cell_ports[cell->type]; - for (auto p : cell->connections()) { - if (p.second.size() > 1) - log_error("Found multi-bit port %s on library cell %s.%s (%s): not supported in EDIF backend!\n", - RTLIL::id2cstr(p.first), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); - lib_cell_ports[cell->type].insert(p.first); - } + for (auto p : cell->connections()) + lib_cell_ports[cell->type][p.first] = GetSize(p.second); } } } @@ -195,12 +194,15 @@ struct EdifBackend : public Backend { for (auto &port_it : cell_it.second) { const char *dir = "INOUT"; if (ct.cell_known(cell_it.first)) { - if (!ct.cell_output(cell_it.first, port_it)) + if (!ct.cell_output(cell_it.first, port_it.first)) dir = "INPUT"; - else if (!ct.cell_input(cell_it.first, port_it)) + else if (!ct.cell_input(cell_it.first, port_it.first)) dir = "OUTPUT"; } - *f << stringf(" (port %s (direction %s))\n", EDIF_DEF(port_it), dir); + if (port_it.second == 1) + *f << stringf(" (port %s (direction %s))\n", EDIF_DEF(port_it.first), dir); + else + *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEF(port_it.first), port_it.second, dir); } *f << stringf(" )\n"); *f << stringf(" )\n"); @@ -300,12 +302,12 @@ struct EdifBackend : public Backend { char digit_str[2] = { "0123456789abcdef"[digit_value], 0 }; hex_string = std::string(digit_str) + hex_string; } - *f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(p.first), hex_string.c_str()); + *f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(p.first), GetSize(p.second.bits), hex_string.c_str()); } *f << stringf(")\n"); for (auto &p : cell->connections()) { RTLIL::SigSpec sig = sigmap(p.second); - for (int i = 0; i < SIZE(sig); i++) + for (int i = 0; i < GetSize(sig); i++) if (sig.size() == 1) net_join_db[sig[i]].insert(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name))); else @@ -345,3 +347,4 @@ struct EdifBackend : public Backend { } } EdifBackend; +PRIVATE_NAMESPACE_END diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc index 48d818d7..814d3e8f 100644 --- a/backends/ilang/ilang_backend.cc +++ b/backends/ilang/ilang_backend.cc @@ -26,7 +26,9 @@ #include "kernel/yosys.h" #include +USING_YOSYS_NAMESPACE using namespace ILANG_BACKEND; +YOSYS_NAMESPACE_BEGIN void ILANG_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int width, int offset, bool autoint) { @@ -101,7 +103,7 @@ void ILANG_BACKEND::dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, boo dump_sigchunk(f, sig.as_chunk(), autoint); } else { f << stringf("{ "); - for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); it++) { + for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) { dump_sigchunk(f, *it, false); f << stringf(" "); } @@ -111,11 +113,9 @@ void ILANG_BACKEND::dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, boo void ILANG_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire) { - std::map sorted_attributes(wire->attributes.begin(), wire->attributes.end()); - - for (auto it = sorted_attributes.begin(); it != sorted_attributes.end(); it++) { - f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str()); - dump_const(f, it->second); + for (auto &it : wire->attributes) { + f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str()); + dump_const(f, it.second); f << stringf("\n"); } f << stringf("%s" "wire ", indent.c_str()); @@ -136,11 +136,9 @@ void ILANG_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL:: void ILANG_BACKEND::dump_memory(std::ostream &f, std::string indent, const RTLIL::Memory *memory) { - std::map sorted_attributes(memory->attributes.begin(), memory->attributes.end()); - - for (auto it = sorted_attributes.begin(); it != sorted_attributes.end(); it++) { - f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str()); - dump_const(f, it->second); + for (auto &it : memory->attributes) { + f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str()); + dump_const(f, it.second); f << stringf("\n"); } f << stringf("%s" "memory ", indent.c_str()); @@ -148,29 +146,27 @@ void ILANG_BACKEND::dump_memory(std::ostream &f, std::string indent, const RTLIL f << stringf("width %d ", memory->width); if (memory->size != 0) f << stringf("size %d ", memory->size); + if (memory->start_offset != 0) + f << stringf("offset %d ", memory->start_offset); f << stringf("%s\n", memory->name.c_str()); } void ILANG_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL::Cell *cell) { - std::map sorted_attributes(cell->attributes.begin(), cell->attributes.end()); - std::map sorted_parameters(cell->parameters.begin(), cell->parameters.end()); - std::map sorted_connections(cell->connections().begin(), cell->connections().end()); - - for (auto it = sorted_attributes.begin(); it != sorted_attributes.end(); it++) { - f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str()); - dump_const(f, it->second); + for (auto &it : cell->attributes) { + f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str()); + dump_const(f, it.second); f << stringf("\n"); } f << stringf("%s" "cell %s %s\n", indent.c_str(), cell->type.c_str(), cell->name.c_str()); - for (auto it = sorted_parameters.begin(); it != sorted_parameters.end(); it++) { - f << stringf("%s parameter%s %s ", indent.c_str(), (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "", it->first.c_str()); - dump_const(f, it->second); + for (auto &it : cell->parameters) { + f << stringf("%s parameter%s %s ", indent.c_str(), (it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "", it.first.c_str()); + dump_const(f, it.second); f << stringf("\n"); } - for (auto it = sorted_connections.begin(); it != sorted_connections.end(); it++) { - f << stringf("%s connect %s ", indent.c_str(), it->first.c_str()); - dump_sigspec(f, it->second); + for (auto &it : cell->connections()) { + f << stringf("%s connect %s ", indent.c_str(), it.first.c_str()); + dump_sigspec(f, it.second); f << stringf("\n"); } f << stringf("%s" "end\n", indent.c_str()); @@ -178,7 +174,7 @@ void ILANG_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL:: void ILANG_BACKEND::dump_proc_case_body(std::ostream &f, std::string indent, const RTLIL::CaseRule *cs) { - for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) + for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) { f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, it->first); @@ -187,13 +183,13 @@ void ILANG_BACKEND::dump_proc_case_body(std::ostream &f, std::string indent, con f << stringf("\n"); } - for (auto it = cs->switches.begin(); it != cs->switches.end(); it++) + for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it) dump_proc_switch(f, indent, *it); } void ILANG_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const RTLIL::SwitchRule *sw) { - for (auto it = sw->attributes.begin(); it != sw->attributes.end(); it++) { + for (auto it = sw->attributes.begin(); it != sw->attributes.end(); ++it) { f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str()); dump_const(f, it->second); f << stringf("\n"); @@ -203,7 +199,7 @@ void ILANG_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const dump_sigspec(f, sw->signal); f << stringf("\n"); - for (auto it = sw->cases.begin(); it != sw->cases.end(); it++) + for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { f << stringf("%s case ", indent.c_str()); for (size_t i = 0; i < (*it)->compare.size(); i++) { @@ -235,7 +231,7 @@ void ILANG_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT case RTLIL::STi: f << stringf("init\n"); break; } - for (auto it = sy->actions.begin(); it != sy->actions.end(); it++) { + for (auto it = sy->actions.begin(); it != sy->actions.end(); ++it) { f << stringf("%s update ", indent.c_str()); dump_sigspec(f, it->first); f << stringf(" "); @@ -246,14 +242,14 @@ void ILANG_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT void ILANG_BACKEND::dump_proc(std::ostream &f, std::string indent, const RTLIL::Process *proc) { - for (auto it = proc->attributes.begin(); it != proc->attributes.end(); it++) { + for (auto it = proc->attributes.begin(); it != proc->attributes.end(); ++it) { f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str()); dump_const(f, it->second); f << stringf("\n"); } f << stringf("%s" "process %s\n", indent.c_str(), proc->name.c_str()); dump_proc_case_body(f, indent + " ", &proc->root_case); - for (auto it = proc->syncs.begin(); it != proc->syncs.end(); it++) + for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it) dump_proc_sync(f, indent + " ", *it); f << stringf("%s" "end\n", indent.c_str()); } @@ -274,7 +270,7 @@ void ILANG_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Modu if (print_header) { - for (auto it = module->attributes.begin(); it != module->attributes.end(); it++) { + for (auto it = module->attributes.begin(); it != module->attributes.end(); ++it) { f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str()); dump_const(f, it->second); f << stringf("\n"); @@ -285,56 +281,36 @@ void ILANG_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Modu if (print_body) { - std::vector sorted_wires; for (auto it : module->wires()) - sorted_wires.push_back(it); - std::sort(sorted_wires.begin(), sorted_wires.end(), RTLIL::sort_by_name_str()); - - std::vector sorted_memories; - for (auto it : module->memories) - sorted_memories.push_back(it.second); - std::sort(sorted_memories.begin(), sorted_memories.end(), RTLIL::sort_by_name_str()); - - std::vector sorted_cells; - for (auto it : module->cells()) - sorted_cells.push_back(it); - std::sort(sorted_cells.begin(), sorted_cells.end(), RTLIL::sort_by_name_str()); - - std::vector sorted_processes; - for (auto it : module->processes) - sorted_processes.push_back(it.second); - std::sort(sorted_processes.begin(), sorted_processes.end(), RTLIL::sort_by_name_str()); - - for (auto it : sorted_wires) if (!only_selected || design->selected(module, it)) { if (only_selected) f << stringf("\n"); dump_wire(f, indent + " ", it); } - for (auto it : sorted_memories) - if (!only_selected || design->selected(module, it)) { + for (auto it : module->memories) + if (!only_selected || design->selected(module, it.second)) { if (only_selected) f << stringf("\n"); - dump_memory(f, indent + " ", it); + dump_memory(f, indent + " ", it.second); } - for (auto it : sorted_cells) + for (auto it : module->cells()) if (!only_selected || design->selected(module, it)) { if (only_selected) f << stringf("\n"); dump_cell(f, indent + " ", it); } - for (auto it : sorted_processes) - if (!only_selected || design->selected(module, it)) { + for (auto it : module->processes) + if (!only_selected || design->selected(module, it.second)) { if (only_selected) f << stringf("\n"); - dump_proc(f, indent + " ", it); + dump_proc(f, indent + " ", it.second); } bool first_conn_line = true; - for (auto it = module->connections().begin(); it != module->connections().end(); it++) { + for (auto it = module->connections().begin(); it != module->connections().end(); ++it) { bool show_conn = !only_selected; if (only_selected) { RTLIL::SigSpec sigs = it->first; @@ -360,11 +336,13 @@ void ILANG_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Modu void ILANG_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n) { +#ifndef NDEBUG int init_autoidx = autoidx; +#endif if (!flag_m) { int count_selected_mods = 0; - for (auto it = design->modules_.begin(); it != design->modules_.end(); it++) { + for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) { if (design->selected_whole_module(it->first)) flag_m = true; if (design->selected(it->second)) @@ -380,7 +358,7 @@ void ILANG_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool onl f << stringf("autoidx %d\n", autoidx); } - for (auto it = design->modules_.begin(); it != design->modules_.end(); it++) { + for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) { if (!only_selected || design->selected(it->second)) { if (only_selected) f << stringf("\n"); @@ -391,6 +369,9 @@ void ILANG_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool onl log_assert(init_autoidx == autoidx); } +YOSYS_NAMESPACE_END +PRIVATE_NAMESPACE_BEGIN + struct IlangBackend : public Backend { IlangBackend() : Backend("ilang", "write design to ilang file") { } virtual void help() @@ -423,6 +404,8 @@ struct IlangBackend : public Backend { } extra_args(f, filename, args, argidx); + design->sort(); + log("Output filename: %s\n", filename.c_str()); *f << stringf("# Generated by %s\n", yosys_version_str); ILANG_BACKEND::dump_design(*f, design, selected, true, false); @@ -447,10 +430,10 @@ struct DumpPass : public Pass { log(" -n\n"); log(" only dump the module headers if the entire module is selected\n"); log("\n"); - log(" -outfile \n"); + log(" -o \n"); log(" write to the specified file.\n"); log("\n"); - log(" -append \n"); + log(" -a \n"); log(" like -outfile but append instead of overwrite\n"); log("\n"); } @@ -463,12 +446,12 @@ struct DumpPass : public Pass { for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; - if (arg == "-outfile" && argidx+1 < args.size()) { + if ((arg == "-o" || arg == "-outfile") && argidx+1 < args.size()) { filename = args[++argidx]; append = false; continue; } - if (arg == "-append" && argidx+1 < args.size()) { + if ((arg == "-a" || arg == "-append") && argidx+1 < args.size()) { filename = args[++argidx]; append = true; continue; @@ -510,3 +493,4 @@ struct DumpPass : public Pass { } } DumpPass; +PRIVATE_NAMESPACE_END diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc index 8502d90f..6d4731e7 100644 --- a/backends/intersynth/intersynth.cc +++ b/backends/intersynth/intersynth.cc @@ -24,6 +24,8 @@ #include "kernel/log.h" #include +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN static std::string netname(std::set &conntypes_code, std::set &celltypes_code, std::set &constcells_code, RTLIL::SigSpec sig) { @@ -215,3 +217,4 @@ struct IntersynthBackend : public Backend { } } IntersynthBackend; +PRIVATE_NAMESPACE_END diff --git a/backends/json/Makefile.inc b/backends/json/Makefile.inc new file mode 100644 index 00000000..a463daf9 --- /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 00000000..7d73fb11 --- /dev/null +++ b/backends/json/json.cc @@ -0,0 +1,407 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct JsonWriter +{ + std::ostream &f; + bool use_selection; + + Design *design; + Module *module; + + SigMap sigmap; + int sigidcounter; + dict sigids; + + JsonWriter(std::ostream &f, bool use_selection) : f(f), use_selection(use_selection) { } + + 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 ¶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()); + f << stringf(" \"parameters\": {"); + write_parameters(c->parameters); + f << stringf("\n },\n"); + f << stringf(" \"attributes\": {"); + write_parameters(c->attributes); + 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 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 }\n"); + f << stringf("}\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("The general syntax of the JSON output created by this command is as follows:\n"); + log("\n"); + log(" {\n"); + log(" \"modules\": {\n"); + log(" : {\n"); + log(" \"ports\": {\n"); + log(" : ,\n"); + log(" ...\n"); + log(" },\n"); + log(" \"cells\": {\n"); + log(" : ,\n"); + log(" ...\n"); + log(" },\n"); + log(" \"netnames\": {\n"); + log(" : ,\n"); + log(" ...\n"); + log(" }\n"); + log(" }\n"); + log(" }\n"); + log(" }\n"); + log("\n"); + log("Where is:\n"); + log("\n"); + log(" {\n"); + log(" \"direction\": <\"input\" | \"output\" | \"inout\">,\n"); + log(" \"bits\": \n"); + log(" }\n"); + log("\n"); + log("And is:\n"); + log("\n"); + log(" {\n"); + log(" \"hide_name\": <1 | 0>,\n"); + log(" \"type\": ,\n"); + log(" \"parameters\": {\n"); + log(" : ,\n"); + log(" ...\n"); + log(" },\n"); + log(" \"attributes\": {\n"); + log(" : ,\n"); + log(" ...\n"); + log(" },\n"); + log(" \"connections\": {\n"); + log(" : ,\n"); + log(" ...\n"); + log(" },\n"); + log(" }\n"); + log("\n"); + log("And is:\n"); + log("\n"); + log(" {\n"); + log(" \"hide_name\": <1 | 0>,\n"); + log(" \"bits\": \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("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 \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("Future version of Yosys might add support for additional fields in the JSON\n"); + log("format. A program processing this format must ignore all unkown fields.\n"); + log("\n"); + } + virtual void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) + { + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-verbose") { + // verbose = true; + // continue; + // } + break; + } + extra_args(f, filename, args, argidx); + + log_header("Executing JSON backend.\n"); + + JsonWriter json_writer(*f, false); + 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 \n"); + log(" write to the specified file.\n"); + log("\n"); + log("See 'help write_json' for a description of the JSON format used.\n"); + log("\n"); + } + virtual void execute(std::vector args, RTLIL::Design *design) + { + std::string filename; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-o" && argidx+1 < args.size()) { + filename = args[++argidx]; + 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); + 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/.gitignore b/backends/smt2/.gitignore new file mode 100644 index 00000000..313ea0a1 --- /dev/null +++ b/backends/smt2/.gitignore @@ -0,0 +1 @@ +test_cells diff --git a/backends/smt2/Makefile.inc b/backends/smt2/Makefile.inc new file mode 100644 index 00000000..4e0a393a --- /dev/null +++ b/backends/smt2/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += backends/smt2/smt2.o + diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc new file mode 100644 index 00000000..3d872c63 --- /dev/null +++ b/backends/smt2/smt2.cc @@ -0,0 +1,711 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct Smt2Worker +{ + CellTypes ct; + SigMap sigmap; + RTLIL::Module *module; + bool bvmode, verbose; + int idcounter; + + std::vector decls, trans; + std::map bit_driver; + std::set exported_cells; + pool recursive_cells, registers; + + std::map> fcache; + std::map bvsizes; + + Smt2Worker(RTLIL::Module *module, bool bvmode, bool verbose) : + ct(module->design), sigmap(module), module(module), bvmode(bvmode), verbose(verbose), idcounter(0) + { + decls.push_back(stringf("(declare-sort |%s_s| 0)\n", log_id(module))); + + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) { + bool is_input = ct.cell_input(cell->type, conn.first); + bool is_output = ct.cell_output(cell->type, conn.first); + if (is_output && !is_input) + for (auto bit : sigmap(conn.second)) { + if (bit_driver.count(bit)) + log_error("Found multiple drivers for %s.\n", log_signal(bit)); + bit_driver[bit] = cell; + } + else if (is_output || !is_input) + log_error("Unsupported or unknown directionality on port %s of cell %s.%s (%s).\n", + log_id(conn.first), log_id(module), log_id(cell), log_id(cell->type)); + } + } + + 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(id, -1); + } + + 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); + + log_assert(bvsizes.count(id) == 0); + bvsizes[id] = GetSize(sig); + + for (int i = 0; i < GetSize(sig); i++) { + log_assert(fcache.count(sig[i]) == 0); + fcache[sig[i]] = std::pair(id, i); + } + } + + 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); + + for (int i = 1; i < GetSize(sig); i++) + sigmap.add(sig[i], RTLIL::State::S0); + } + + std::string get_bool(RTLIL::SigBit bit, const char *state_name = "state") + { + sigmap.apply(bit); + + if (bit.wire == nullptr) + return bit == RTLIL::State::S1 ? "true" : "false"; + + if (bit_driver.count(bit)) + export_cell(bit_driver.at(bit)); + 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++); + } + + auto f = fcache.at(bit); + if (f.second >= 0) + return stringf("(= ((_ extract %d %d) (|%s#%d| %s)) #b1)", f.second, f.second, log_id(module), f.first, state_name); + return stringf("(|%s#%d| %s)", log_id(module), f.first, state_name); + } + + std::string get_bool(RTLIL::SigSpec sig, const char *state_name = "state") + { + return get_bool(sig.to_single_sigbit(), state_name); + } + + std::string get_bv(RTLIL::SigSpec sig, const char *state_name = "state") + { + log_assert(bvmode); + sigmap.apply(sig); + + std::vector subexpr; + + 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) + { + if (sig[i].wire == nullptr) { + while (i+j < GetSize(sig) && sig[i+j].wire == nullptr) j++; + subexpr.push_back("#b"); + for (int k = i+j-1; k >= i; k--) + subexpr.back() += sig[k] == RTLIL::State::S1 ? "1" : "0"; + continue; + } + + if (fcache.count(sig[i]) && fcache.at(sig[i]).second == -1) { + subexpr.push_back(stringf("(ite %s #b1 #b0)", get_bool(sig[i], state_name).c_str())); + continue; + } + + if (fcache.count(sig[i])) { + auto t1 = fcache.at(sig[i]); + while (i+j < GetSize(sig)) { + if (fcache.count(sig[i+j]) == 0) + break; + auto t2 = fcache.at(sig[i+j]); + if (t1.first != t2.first) + break; + if (t1.second+j != t2.second) + break; + j++; + } + if (t1.second == 0 && j == bvsizes.at(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| %s))", + t1.second + j - 1, t1.second, log_id(module), t1.first, state_name)); + continue; + } + + std::set seen_bits = { sig[i] }; + 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| %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--) + expr += " " + subexpr[i]; + return expr + ")"; + } else { + log_assert(GetSize(subexpr) == 1); + return subexpr[0]; + } + } + + void export_gate(RTLIL::Cell *cell, std::string expr) + { + RTLIL::SigBit bit = sigmap(cell->getPort("\\Y").to_single_sigbit()); + std::string processed_expr; + + for (char ch : expr) { + if (ch == 'A') processed_expr += get_bool(cell->getPort("\\A")); + else if (ch == 'B') processed_expr += get_bool(cell->getPort("\\B")); + else if (ch == 'C') processed_expr += get_bool(cell->getPort("\\C")); + else if (ch == 'D') processed_expr += get_bool(cell->getPort("\\D")); + else if (ch == 'S') processed_expr += get_bool(cell->getPort("\\S")); + 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++); + recursive_cells.erase(cell); + } + + void export_bvop(RTLIL::Cell *cell, std::string expr, char type = 0) + { + RTLIL::SigSpec sig_a, sig_b; + RTLIL::SigSpec sig_y = sigmap(cell->getPort("\\Y")); + bool is_signed = cell->getParam("\\A_SIGNED").as_bool(); + 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"))); + } + + if (cell->hasPort("\\A")) { + sig_a = cell->getPort("\\A"); + sig_a.extend_u0(width, is_signed); + } + + if (cell->hasPort("\\B")) { + sig_b = cell->getPort("\\B"); + sig_b.extend_u0(width, is_signed && !(type == 's')); + } + + std::string processed_expr; + + for (char ch : expr) { + if (ch == 'A') processed_expr += get_bv(sig_a); + else if (ch == 'B') processed_expr += get_bv(sig_b); + else if (ch == 'L') processed_expr += is_signed ? "a" : "l"; + else if (ch == 'U') processed_expr += is_signed ? "s" : "u"; + else processed_expr += ch; + } + + 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))); + register_boolvec(sig_y, idcounter++); + } else { + decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", + log_id(module), idcounter, log_id(module), GetSize(sig_y), processed_expr.c_str(), log_signal(sig_y))); + register_bv(sig_y, idcounter++); + } + + recursive_cells.erase(cell); + } + + void export_reduce(RTLIL::Cell *cell, std::string expr, bool identity_val) + { + RTLIL::SigSpec sig_y = sigmap(cell->getPort("\\Y")); + std::string processed_expr; + + for (char ch : expr) + if (ch == 'A' || ch == 'B') { + RTLIL::SigSpec sig = sigmap(cell->getPort(stringf("\\%c", ch))); + for (auto bit : sig) + processed_expr += " " + get_bool(bit); + if (GetSize(sig) == 1) + processed_expr += identity_val ? " true" : " false"; + } 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++); + 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_") + { + 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; + } + + if (cell->type == "$_BUF_") return export_gate(cell, "A"); + if (cell->type == "$_NOT_") return export_gate(cell, "(not A)"); + if (cell->type == "$_AND_") return export_gate(cell, "(and A B)"); + if (cell->type == "$_NAND_") return export_gate(cell, "(not (and A B))"); + if (cell->type == "$_OR_") return export_gate(cell, "(or A B)"); + if (cell->type == "$_NOR_") return export_gate(cell, "(not (or A B))"); + if (cell->type == "$_XOR_") return export_gate(cell, "(xor A B)"); + if (cell->type == "$_XNOR_") return export_gate(cell, "(not (xor A B))"); + if (cell->type == "$_MUX_") return export_gate(cell, "(ite S B A)"); + if (cell->type == "$_AOI3_") return export_gate(cell, "(not (or (and A B) C))"); + if (cell->type == "$_OAI3_") return export_gate(cell, "(not (and (or A B) C))"); + 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 + + 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)); + + 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 + + log_error("Unsupported cell type %s for cell %s.%s.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + + void run() + { + if (verbose) log("=> export logic driving outputs\n"); + + for (auto wire : module->wires()) + if (wire->port_id || wire->get_bool_attribute("\\keep")) { + RTLIL::SigSpec sig = sigmap(wire); + 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())); + } else { + for (int i = 0; i < GetSize(sig); i++) + if (GetSize(sig) > 1) + decls.push_back(stringf("(define-fun |%s_n %s %d| ((state |%s_s|)) Bool %s)\n", + log_id(module), log_id(wire), i, log_id(module), get_bool(sig[i]).c_str())); + else + decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) Bool %s)\n", + 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 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 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 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")))); + } + } + } + + 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) + { + for (auto it : decls) + f << it; + + 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"; + for (auto it : trans) + f << it; + f << "))"; + } else + if (GetSize(trans) == 1) + f << "\n" + trans.front() + ")"; + else + f << "true)"; + f << stringf(" ; end of module %s\n", log_id(module)); + } +}; + +struct Smt2Backend : public Backend { + Smt2Backend() : Backend("smt2", "write design to SMT-LIBv2 file") { } + virtual void help() + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + 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("'' this will declare the sort '_s' (state of the module) and the\n"); + log("functions operating on that state.\n"); + log("\n"); + log("The '_s' sort represents a module state. Additional '_n' functions\n"); + log("are provided that can be used to access the values of the signals in the module.\n"); + log("Only ports, and signals with the 'keep' attribute set are made available via\n"); + log("such functions. Without the -bv option, multi-bit wires are exported as\n"); + log("separate functions of type Bool for the individual bits. With the -bv option\n"); + log("multi-bit wires are exported as single functions of type BitVec.\n"); + log("\n"); + log("The '_t' function evaluates to 'true' when the given pair of states\n"); + log("describes a valid state transition.\n"); + log("\n"); + log("The '_a' function evaluates to 'true' when the given state satisfies\n"); + log("the asserts in the module.\n"); + log("\n"); + log("The '_u' function evaluates to 'true' when the given state satisfies\n"); + log("the assumptions in the module.\n"); + log("\n"); + log("The '_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(" -tpl \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("[1] For more information on SMT-LIBv2 visit http://smt-lib.org/ or read David\n"); + log("R. Cok's tutorial: http://www.grammatech.com/resources/smt/SMTLIBTutorial.pdf\n"); + log("\n"); + log("---------------------------------------------------------------------------\n"); + log("\n"); + log("Example:\n"); + log("\n"); + log("Consider the following module (test.v). We want to prove that the output can\n"); + log("never transition from a non-zero value to a zero value.\n"); + log("\n"); + log(" module test(input clk, output reg [3:0] y);\n"); + log(" always @(posedge clk)\n"); + log(" y <= (y << 1) | ^y;\n"); + log(" endmodule\n"); + log("\n"); + log("For this proof we create the following template (test.tpl).\n"); + log("\n"); + log(" ; we need QF_UFBV for this poof\n"); + log(" (set-logic QF_UFBV)\n"); + log("\n"); + log(" ; insert the auto-generated code here\n"); + log(" %%%%\n"); + log("\n"); + log(" ; declare two state variables s1 and s2\n"); + log(" (declare-fun s1 () test_s)\n"); + log(" (declare-fun s2 () test_s)\n"); + log("\n"); + log(" ; state s2 is the successor of state s1\n"); + log(" (assert (test_t s1 s2))\n"); + log("\n"); + log(" ; we are looking for a model with y non-zero in s1\n"); + log(" (assert (distinct (|test_n y| s1) #b0000))\n"); + log("\n"); + log(" ; we are looking for a model with y zero in s2\n"); + log(" (assert (= (|test_n y| s2) #b0000))\n"); + log("\n"); + log(" ; is there such a model?\n"); + log(" (check-sat)\n"); + log("\n"); + 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 -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"); + log("from non-zero to zero in the test design.\n"); + log("\n"); + } + virtual void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) + { + std::ifstream template_f; + bool bvmode = false, verbose = false; + + log_header("Executing SMT2 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] == "-bv") { + bvmode = true; + continue; + } + if (args[argidx] == "-verbose") { + verbose = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + 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.substr(indent, 2) == "%%") + break; + *f << line << std::endl; + } + } + + *f << stringf("; SMT-LIBv2 description generated by %s\n", yosys_version_str); + + for (auto module : design->modules()) + { + if (module->get_bool_attribute("\\blackbox") || module->has_memories_warn() || module->has_processes_warn()) + continue; + + log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module)); + + Smt2Worker worker(module, bvmode, verbose); + worker.run(); + worker.write(*f); + } + + *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; + } + } +} Smt2Backend; + +PRIVATE_NAMESPACE_END diff --git a/backends/smt2/test_cells.sh b/backends/smt2/test_cells.sh new file mode 100644 index 00000000..34adb7af --- /dev/null +++ b/backends/smt2/test_cells.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +set -ex + +rm -rf test_cells +mkdir test_cells +cd test_cells + +../../../yosys -p 'test_cell -muxdiv -w test all /$alu /$macc /$fa /$lcu /$lut /$shift /$shiftx' + +cat > miter.tpl <<- EOT +; #model# (set-option :produce-models true) +(set-logic QF_UFBV) +%% +(declare-fun s () miter_s) +(assert (|miter_n trigger| s)) +(check-sat) +; #model# (get-value ((|miter_n in_A| s) (|miter_n in_B| s) (|miter_n gold_Y| s) (|miter_n gate_Y| s) (|miter_n trigger| s))) +EOT + +for x in $(set +x; ls test_*.il | sort -R); do + x=${x%.il} + cat > $x.ys <<- EOT + read_ilang $x.il + copy gold gate + + cd gate + techmap; opt; abc;; + cd .. + + miter -equiv -flatten -make_outputs gold gate miter + hierarchy -check -top miter + + dump + write_smt2 -bv -tpl miter.tpl $x.smt2 + EOT + ../../../yosys -q $x.ys + if ! cvc4 $x.smt2 > $x.result; then + cat $x.result + exit 1 + fi + if ! grep unsat $x.result; then + echo "Proof failed! Extracting model..." + sed -i 's/^; #model# //' $x.smt2 + cvc4 $x.smt2 + exit 1 + fi +done + +set +x +echo "" +echo " All tests passed." +echo "" +exit 0 + diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc index b057063c..2c614178 100644 --- a/backends/spice/spice.cc +++ b/backends/spice/spice.cc @@ -24,6 +24,9 @@ #include "kernel/log.h" #include +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) { if (s.wire) { @@ -55,7 +58,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", + 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)); for (auto &conn : cell->connections()) { RTLIL::SigSpec sig = sigmap(conn.second); @@ -81,7 +84,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De RTLIL::SigSpec sig(RTLIL::State::Sz, wire->width); if (cell->hasPort(wire->name)) { sig = sigmap(cell->getPort(wire->name)); - sig.extend(wire->width, false); + sig.extend_u0(wire->width, false); } port_sigs.push_back(sig); } @@ -231,3 +234,4 @@ struct SpiceBackend : public Backend { } } SpiceBackend; +PRIVATE_NAMESPACE_END diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index bbdbbbfa..ba57e881 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -26,7 +26,6 @@ * */ -#include "verilog_backend.h" #include "kernel/register.h" #include "kernel/celltypes.h" #include "kernel/log.h" @@ -35,7 +34,8 @@ #include #include -namespace { +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN bool norename, noattr, attr2comment, noexpr; int auto_name_counter, auto_name_offset, auto_name_digits; @@ -51,16 +51,17 @@ void reset_auto_counter_id(RTLIL::IdString id, bool may_rename) if (*str == '$' && may_rename && !norename) auto_name_map[id] = auto_name_counter++; - if (str[0] != '_' && str[1] != 0) + if (str[0] != '\\' || str[1] != '_' || str[2] == 0) return; - for (int i = 0; str[i] != 0; i++) { - if (str[i] == '_') + + for (int i = 2; str[i] != 0; i++) { + if (str[i] == '_' && str[i+1] == 0) continue; if (str[i] < '0' || str[i] > '9') return; } - int num = atoi(str+1); + int num = atoi(str+2); if (num >= auto_name_offset) auto_name_offset = num + 1; } @@ -73,22 +74,22 @@ void reset_auto_counter(RTLIL::Module *module) reset_auto_counter_id(module->name, false); - for (auto it = module->wires_.begin(); it != module->wires_.end(); it++) + for (auto it = module->wires_.begin(); it != module->wires_.end(); ++it) reset_auto_counter_id(it->second->name, true); - for (auto it = module->cells_.begin(); it != module->cells_.end(); it++) { + for (auto it = module->cells_.begin(); it != module->cells_.end(); ++it) { reset_auto_counter_id(it->second->name, true); reset_auto_counter_id(it->second->type, false); } - for (auto it = module->processes.begin(); it != module->processes.end(); it++) + for (auto it = module->processes.begin(); it != module->processes.end(); ++it) reset_auto_counter_id(it->second->name, false); auto_name_digits = 1; for (size_t i = 10; i < auto_name_offset + auto_name_map.size(); i = i*10) auto_name_digits++; - for (auto it = auto_name_map.begin(); it != auto_name_map.end(); it++) + for (auto it = auto_name_map.begin(); it != auto_name_map.end(); ++it) log(" renaming `%s' to `_%0*d_'.\n", it->first.c_str(), auto_name_digits, auto_name_offset + it->second); } @@ -150,7 +151,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; @@ -166,7 +167,7 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o if (data.bits[i] == RTLIL::S1) val |= 1 << (i - offset); } - f << stringf("32'%sd%d", set_signed ? "s" : "", val); + f << stringf("32'%sd %d", set_signed ? "s" : "", val); } else { dump_bits: f << stringf("%d'%sb", width, set_signed ? "s" : ""); @@ -198,6 +199,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]; } @@ -236,7 +239,7 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) dump_sigchunk(f, sig.as_chunk()); } else { f << stringf("{ "); - for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); it++) { + for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) { if (it != sig.chunks().rbegin()) f << stringf(", "); dump_sigchunk(f, *it, true); @@ -245,14 +248,19 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) } } -void dump_attributes(std::ostream &f, std::string indent, std::map &attributes, char term = '\n') +void dump_attributes(std::ostream &f, std::string indent, dict &attributes, char term = '\n', bool modattr = false) { if (noattr) return; - for (auto it = attributes.begin(); it != attributes.end(); it++) { + for (auto it = attributes.begin(); it != attributes.end(); ++it) { f << stringf("%s" "%s %s", indent.c_str(), attr2comment ? "/*" : "(*", id(it->first).c_str()); f << stringf(" = "); - dump_const(f, it->second); + if (modattr && (it->second == Const(0, 1) || it->second == Const(0))) + f << stringf(" 0 "); + else if (modattr && (it->second == Const(1, 1) || it->second == Const(1))) + f << stringf(" 1 "); + else + dump_const(f, it->second, -1, 0, false, false, attr2comment); f << stringf(" %s%c", attr2comment ? "*/" : "*)", term); } } @@ -315,7 +323,7 @@ std::string cellname(RTLIL::Cell *cell) if (!norename && cell->name[0] == '$' && reg_ct.count(cell->type) && cell->hasPort("\\Q")) { RTLIL::SigSpec sig = cell->getPort("\\Q"); - if (SIZE(sig) != 1 || sig.is_fully_const()) + if (GetSize(sig) != 1 || sig.is_fully_const()) goto no_special_reg_name; RTLIL::Wire *wire = sig[0].wire; @@ -663,10 +671,60 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } - if (cell->type == "$dff" || cell->type == "$adff") + if (cell->type == "$dffsr") { - RTLIL::SigSpec sig_clk, sig_arst, val_arst; - bool pol_clk, pol_arst = false; + 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; + bool pol_clk, pol_arst = false, pol_en = false; sig_clk = cell->getPort("\\CLK"); pol_clk = cell->parameters["\\CLK_POLARITY"].as_bool(); @@ -677,6 +735,11 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) val_arst = RTLIL::SigSpec(cell->parameters["\\ARST_VALUE"]); } + if (cell->type == "$dffe") { + sig_en = cell->getPort("\\EN"); + pol_en = cell->parameters["\\EN_POLARITY"].as_bool(); + } + std::string reg_name = cellname(cell); bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name); @@ -701,6 +764,12 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s" " else\n", indent.c_str()); } + if (cell->type == "$dffe") { + f << stringf("%s" " if (%s", indent.c_str(), pol_en ? "" : "!"); + dump_sigspec(f, sig_en); + f << stringf(")\n"); + } + f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str()); dump_cell_expr_port(f, cell, "D", false); f << stringf(";\n"); @@ -715,7 +784,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) } // FIXME: $_SR_[PN][PN]_, $_DLATCH_[PN]_, $_DLATCHSR_[PN][PN][PN]_ - // FIXME: $sr, $dffsr, $dlatch, $memrd, $memwr, $mem, $fsm + // FIXME: $sr, $dlatch, $memrd, $memwr, $mem, $fsm return false; } @@ -732,12 +801,12 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->parameters.size() > 0) { f << stringf(" #("); - for (auto it = cell->parameters.begin(); it != cell->parameters.end(); it++) { + for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) { if (it != cell->parameters.begin()) f << stringf(","); f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str()); bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0; - dump_const(f, it->second, -1, 0, !is_signed, is_signed); + dump_const(f, it->second, -1, 0, false, is_signed); f << stringf(")"); } f << stringf("\n%s" ")", indent.c_str()); @@ -754,7 +823,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) for (int i = 1; true; i++) { char str[16]; snprintf(str, 16, "$%d", i); - for (auto it = cell->connections().begin(); it != cell->connections().end(); it++) { + for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) { if (it->first != str) continue; if (!first_arg) @@ -768,7 +837,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) break; found_numbered_port:; } - for (auto it = cell->connections().begin(); it != cell->connections().end(); it++) { + for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) { if (numbered_ports.count(it->first)) continue; if (!first_arg) @@ -800,7 +869,7 @@ void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bo if (!omit_trailing_begin && number_of_stmts >= 2) f << stringf("%s" "begin\n", indent.c_str()); - for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) { + for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) { if (it->first.size() == 0) continue; f << stringf("%s ", indent.c_str()); @@ -810,7 +879,7 @@ void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bo f << stringf(";\n"); } - for (auto it = cs->switches.begin(); it != cs->switches.end(); it++) + for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it) dump_proc_switch(f, indent + " ", *it); if (!omit_trailing_begin && number_of_stmts == 0) @@ -824,7 +893,7 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw { if (sw->signal.size() == 0) { f << stringf("%s" "begin\n", indent.c_str()); - for (auto it = sw->cases.begin(); it != sw->cases.end(); it++) { + for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { if ((*it)->compare.size() == 0) dump_case_body(f, indent + " ", *it); } @@ -836,7 +905,7 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw dump_sigspec(f, sw->signal); f << stringf(")\n"); - for (auto it = sw->cases.begin(); it != sw->cases.end(); it++) { + 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"); @@ -856,11 +925,11 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw void case_body_find_regs(RTLIL::CaseRule *cs) { - for (auto it = cs->switches.begin(); it != cs->switches.end(); it++) + for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it) for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++) case_body_find_regs(*it2); - for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) { + for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) { for (auto &c : it->first.chunks()) if (c.wire != NULL) reg_wires.insert(c.wire->name); @@ -871,7 +940,7 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo { if (find_regs) { case_body_find_regs(&proc->root_case); - for (auto it = proc->syncs.begin(); it != proc->syncs.end(); it++) + for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it) for (auto it2 = (*it)->actions.begin(); it2 != (*it)->actions.end(); it2++) { for (auto &c : it2->first.chunks()) if (c.wire != NULL) @@ -925,7 +994,7 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo } } - for (auto it = sync->actions.begin(); it != sync->actions.end(); it++) { + for (auto it = sync->actions.begin(); it != sync->actions.end(); ++it) { if (it->first.size() == 0) continue; f << stringf("%s ", indent.c_str()); @@ -946,7 +1015,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) active_module = module; f << stringf("\n"); - for (auto it = module->processes.begin(); it != module->processes.end(); it++) + for (auto it = module->processes.begin(); it != module->processes.end(); ++it) dump_process(f, indent + " ", it->second, true); if (!noexpr) @@ -979,12 +1048,12 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) } } - dump_attributes(f, indent, module->attributes); + dump_attributes(f, indent, module->attributes, '\n', true); f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str()); bool keep_running = true; for (int port_id = 1; keep_running; port_id++) { keep_running = false; - for (auto it = module->wires_.begin(); it != module->wires_.end(); it++) { + for (auto it = module->wires_.begin(); it != module->wires_.end(); ++it) { RTLIL::Wire *wire = it->second; if (wire->port_id == port_id) { if (port_id != 1) @@ -997,27 +1066,25 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) } f << stringf(");\n"); - for (auto it = module->wires_.begin(); it != module->wires_.end(); it++) + for (auto it = module->wires_.begin(); it != module->wires_.end(); ++it) dump_wire(f, indent + " ", it->second); - for (auto it = module->memories.begin(); it != module->memories.end(); it++) + for (auto it = module->memories.begin(); it != module->memories.end(); ++it) dump_memory(f, indent + " ", it->second); - for (auto it = module->cells_.begin(); it != module->cells_.end(); it++) + for (auto it = module->cells_.begin(); it != module->cells_.end(); ++it) dump_cell(f, indent + " ", it->second); - for (auto it = module->processes.begin(); it != module->processes.end(); it++) + for (auto it = module->processes.begin(); it != module->processes.end(); ++it) dump_process(f, indent + " ", it->second); - for (auto it = module->connections().begin(); it != module->connections().end(); it++) + for (auto it = module->connections().begin(); it != module->connections().end(); ++it) dump_conn(f, indent + " ", it->first, it->second); f << stringf("%s" "endmodule\n", indent.c_str()); active_module = NULL; } -} /* namespace */ - struct VerilogBackend : public Backend { VerilogBackend() : Backend("verilog", "write design to verilog file") { } virtual void help() @@ -1122,8 +1189,10 @@ struct VerilogBackend : public Backend { } extra_args(f, filename, args, argidx); + design->sort(); + *f << stringf("/* Generated by %s */\n", yosys_version_str); - for (auto it = design->modules_.begin(); it != design->modules_.end(); it++) { + for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) { if (it->second->get_bool_attribute("\\blackbox") != blackboxes) continue; if (selected && !design->selected_whole_module(it->first)) { @@ -1139,3 +1208,4 @@ struct VerilogBackend : public Backend { } } VerilogBackend; +PRIVATE_NAMESPACE_END diff --git a/backends/verilog/verilog_backend.h b/backends/verilog/verilog_backend.h deleted file mode 100644 index 7e6ef5ab..00000000 --- a/backends/verilog/verilog_backend.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * yosys -- Yosys Open SYnthesis Suite - * - * Copyright (C) 2012 Clifford Wolf - * - * 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. - * - * --- - * - * 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). - * - */ - -#ifndef VERILOG_BACKEND_H -#define VERILOG_BACKEND_H - -#include "kernel/yosys.h" - -namespace VERILOG_BACKEND { - void verilog_backend(std::ostream &f, std::vector args, RTLIL::Design *design); -} - -#endif diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 1e43875a..0b63248d 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -26,13 +26,18 @@ * */ -#include "kernel/log.h" +#include "kernel/yosys.h" #include "libs/sha1/sha1.h" #include "ast.h" #include #include -#include + +#if defined(__APPLE__) +# include +#else +# include +#endif YOSYS_NAMESPACE_BEGIN @@ -48,12 +53,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 current_scope; - const std::map *genRTLIL_subst_ptr = NULL; + const dict *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; } @@ -85,6 +90,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) @@ -127,6 +133,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) @@ -175,6 +182,10 @@ bool AstNode::get_bool_attribute(RTLIL::IdString id) // (the optional child arguments make it easier to create AST trees) AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2) { + static unsigned int hashidx_count = 123456789; + hashidx_count = mkhash_xorshift(hashidx_count); + hashidx_ = hashidx_count; + this->type = type; filename = current_filename; linenum = get_line_num(); @@ -267,7 +278,7 @@ void AstNode::dumpAst(FILE *f, std::string indent) bits[i-1] == RTLIL::S1 ? '1' : bits[i-1] == RTLIL::Sx ? 'x' : bits[i-1] == RTLIL::Sz ? 'z' : '?'); - fprintf(f, "'(%zd)", bits.size()); + fprintf(f, "'(%d)", GetSize(bits)); } if (is_input) fprintf(f, " input"); @@ -471,7 +482,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) else if (bits.size() == 32) fprintf(f, "%d", RTLIL::Const(bits).as_int()); else - fprintf(f, "%zd'b %s", bits.size(), RTLIL::Const(bits).as_string().c_str()); + fprintf(f, "%d'b %s", GetSize(bits), RTLIL::Const(bits).as_string().c_str()); break; case AST_REALVALUE: @@ -701,6 +712,8 @@ AstNode *AstNode::mkconst_bits(const std::vector &v, bool is_signe AstNode *AstNode::mkconst_str(const std::vector &v) { AstNode *node = mkconst_str(RTLIL::Const(v).decode_string()); + while (GetSize(node->bits) < GetSize(v)) + node->bits.push_back(RTLIL::State::S0); log_assert(node->bits == v); return node; } @@ -946,6 +959,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; @@ -957,13 +971,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; @@ -1011,7 +1026,7 @@ AstModule::~AstModule() } // create a new parametric module (when needed) and return the name of the generated module -RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::map parameters) +RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict parameters) { std::string stripped_name = name.str(); @@ -1025,6 +1040,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, std::mapast = 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 0a401673..d57e91e5 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -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, @@ -142,6 +144,10 @@ namespace AST // The AST is built using instances of this struct struct AstNode { + // for dict<> and pool<> + unsigned int hashidx_; + unsigned int hash() const { return hashidx_; } + // this nodes type AstNodeType type; @@ -204,11 +210,13 @@ 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); void expand_genblock(std::string index_var, std::string prefix, std::map &name_map); void replace_ids(const std::string &prefix, const std::map &rules); - void mem2reg_as_needed_pass1(std::map> &mem2reg_places, - std::map &mem2reg_flags, std::map &proc_flags, uint32_t &status_flags); - void mem2reg_as_needed_pass2(std::set &mem2reg_set, AstNode *mod, AstNode *block); + void mem2reg_as_needed_pass1(dict> &mem2reg_places, + dict &mem2reg_flags, dict &proc_flags, uint32_t &status_flags); + void mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, AstNode *block); + bool mem2reg_check(pool &mem2reg_set); void meminfo(int &mem_width, int &mem_size, int &addr_bits); // additional functionality for evaluating constant functions @@ -229,7 +237,7 @@ namespace AST // for expressions the resulting signal vector is returned // all generated cell instances, etc. are written to the RTLIL::Module pointed to by AST_INTERNAL::current_module RTLIL::SigSpec genRTLIL(int width_hint = -1, bool sign_hint = false); - RTLIL::SigSpec genWidthRTLIL(int width, const std::map *new_subst_ptr = NULL); + RTLIL::SigSpec genWidthRTLIL(int width, const dict *new_subst_ptr = NULL); // compare AST nodes bool operator==(const AstNode &other) const; @@ -258,15 +266,15 @@ 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 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, std::map parameters); + virtual RTLIL::IdString derive(RTLIL::Design *design, dict parameters); virtual RTLIL::Module *clone() const; }; @@ -288,12 +296,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 current_scope; - extern const std::map *genRTLIL_subst_ptr; + extern const dict *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 2eb104fa..e566d653 100644 --- a/frontends/ast/dpicall.cc +++ b/frontends/ast/dpicall.cc @@ -24,6 +24,8 @@ #include #include +YOSYS_NAMESPACE_BEGIN + typedef void (*ffi_fptr) (); static ffi_fptr resolve_fn (std::string symbol_name) @@ -73,8 +75,8 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, log("Calling DPI function `%s' and returning `%s':\n", fname.c_str(), rtype.c_str()); - log_assert(SIZE(args) == SIZE(argtypes)); - for (int i = 0; i < SIZE(args); i++) { + log_assert(GetSize(args) == GetSize(argtypes)); + for (int i = 0; i < GetSize(args); i++) { if (argtypes[i] == "real") { log(" arg %d (%s): %f\n", i, argtypes[i].c_str(), args[i]->asReal(args[i]->is_signed)); value_store[i].f64 = args[i]->asReal(args[i]->is_signed); @@ -129,12 +131,18 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, return newNode; } +YOSYS_NAMESPACE_END + #else /* YOSYS_ENABLE_PLUGINS */ +YOSYS_NAMESPACE_BEGIN + AST::AstNode *AST::dpi_call(const std::string&, const std::string &fname, const std::vector&, const std::vector&) { log_error("Can't call DPI function `%s': this version of yosys is built without plugin support\n", fname.c_str()); } +YOSYS_NAMESPACE_END + #endif /* YOSYS_ENABLE_PLUGINS */ diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index f87a68f6..8ed8c673 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -73,7 +73,7 @@ static RTLIL::SigSpec uniop2rtlil(AstNode *that, std::string type, int result_wi static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_signed) { if (width <= sig.size()) { - sig.extend(width, is_signed); + sig.extend_u0(width, is_signed); return; } @@ -254,7 +254,7 @@ struct AST_INTERNAL::ProcessGenerator // create initial assignments for the temporary signals if ((flag_nolatches || always->get_bool_attribute("\\nolatches") || current_module->get_bool_attribute("\\nolatches")) && !found_clocked_sync) { - subst_rvalue_map = subst_lvalue_from.to_sigbit_map(RTLIL::SigSpec(RTLIL::State::Sx, SIZE(subst_lvalue_from))); + subst_rvalue_map = subst_lvalue_from.to_sigbit_dict(RTLIL::SigSpec(RTLIL::State::Sx, GetSize(subst_lvalue_from))); } else { addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from); } @@ -289,8 +289,8 @@ struct AST_INTERNAL::ProcessGenerator { RTLIL::SigSpec new_lhs, new_rhs; - log_assert(SIZE(lhs) == SIZE(rhs)); - for (int i = 0; i < SIZE(lhs); i++) { + log_assert(GetSize(lhs) == GetSize(rhs)); + for (int i = 0; i < GetSize(lhs); i++) { if (lhs[i].wire == nullptr) continue; new_lhs.append(lhs[i]); @@ -306,7 +306,7 @@ struct AST_INTERNAL::ProcessGenerator { std::vector chunks = sig.chunks(); - for (int i = 0; i < SIZE(chunks); i++) + for (int i = 0; i < GetSize(chunks); i++) { RTLIL::SigChunk &chunk = chunks[i]; if (chunk.wire == NULL) @@ -430,7 +430,7 @@ struct AST_INTERNAL::ProcessGenerator lvalue.replace(subst_lvalue_map.stdmap()); if (ast->type == AST_ASSIGN_EQ) { - for (int i = 0; i < SIZE(unmapped_lvalue); i++) + for (int i = 0; i < GetSize(unmapped_lvalue); i++) subst_rvalue_map.set(unmapped_lvalue[i], rvalue[i]); } @@ -472,7 +472,7 @@ struct AST_INTERNAL::ProcessGenerator subst_lvalue_map.save(); subst_rvalue_map.save(); - for (int i = 0; i < SIZE(this_case_eq_lvalue); i++) + for (int i = 0; i < GetSize(this_case_eq_lvalue); i++) subst_lvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]); RTLIL::CaseRule *backup_case = current_case; @@ -507,7 +507,7 @@ struct AST_INTERNAL::ProcessGenerator sw->cases.push_back(default_case); } - for (int i = 0; i < SIZE(this_case_eq_lvalue); i++) + for (int i = 0; i < GetSize(this_case_eq_lvalue); i++) subst_rvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]); this_case_eq_lvalue.replace(subst_lvalue_map.stdmap()); @@ -567,9 +567,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]; @@ -839,14 +841,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) memory->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); memory->name = str; memory->width = children[0]->range_left - children[0]->range_right + 1; - memory->start_offset = children[0]->range_right; - memory->size = children[1]->range_left - children[1]->range_right; + if (children[1]->range_right < children[1]->range_left) { + memory->start_offset = children[1]->range_right; + memory->size = children[1]->range_left - children[1]->range_right + 1; + } else { + memory->start_offset = children[1]->range_left; + memory->size = children[1]->range_right - children[1]->range_left + 1; + } current_module->memories[memory->name] = memory; - if (memory->size < 0) - memory->size *= -1; - memory->size += std::min(children[1]->range_left, children[1]->range_right) + 1; - for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) log_error("Attribute `%s' with non-constant value at %s:%d!\n", @@ -869,7 +872,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_REALVALUE: { RTLIL::SigSpec sig = realAsConst(width_hint); - log("Warning: converting real value %e to binary %s at %s:%d.\n", + log_warning("converting real value %e to binary %s at %s:%d.\n", realvalue, log_signal(sig), filename.c_str(), linenum); return sig; } @@ -890,7 +893,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); wire->name = str; if (flag_autowire) - log("Warning: Identifier `%s' is implicitly declared at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + log_warning("Identifier `%s' is implicitly declared at %s:%d.\n", str.c_str(), filename.c_str(), linenum); else log_error("Identifier `%s' is implicitly declared at %s:%d and `default_nettype is set to none.\n", str.c_str(), filename.c_str(), linenum); } @@ -941,7 +944,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) shift_val = current_module->Sub(NEW_ID, RTLIL::SigSpec(source_width - width), shift_val, fake_ast->children[1]->is_signed); fake_ast->children[1]->is_signed = true; } - if (SIZE(shift_val) >= 32) + if (GetSize(shift_val) >= 32) fake_ast->children[1]->is_signed = true; RTLIL::SigSpec sig = binop2rtlil(fake_ast, "$shiftx", width, fake_ast->children[0]->genRTLIL(), shift_val); delete left_at_zero_ast; @@ -955,10 +958,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) chunk.offset = (id2ast->range_left - id2ast->range_right + 1) - (chunk.offset + chunk.width); if (chunk.offset >= source_width || chunk.offset + chunk.width < 0) { if (chunk.width == 1) - log("Warning: Range select out of bounds on signal `%s' at %s:%d: Setting result bit to undef.\n", + log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting result bit to undef.\n", str.c_str(), filename.c_str(), linenum); else - log("Warning: Range select out of bounds on signal `%s' at %s:%d: Setting all %d result bits to undef.\n", + log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting all %d result bits to undef.\n", str.c_str(), filename.c_str(), linenum, chunk.width); chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width); } else { @@ -972,10 +975,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) chunk.offset += add_undef_bits_lsb; } if (add_undef_bits_lsb) - log("Warning: Range select out of bounds on signal `%s' at %s:%d: Setting %d LSB bits to undef.\n", + log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting %d LSB bits to undef.\n", str.c_str(), filename.c_str(), linenum, add_undef_bits_lsb); if (add_undef_bits_msb) - log("Warning: Range select out of bounds on signal `%s' at %s:%d: Setting %d MSB bits to undef.\n", + log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting %d MSB bits to undef.\n", str.c_str(), filename.c_str(), linenum, add_undef_bits_msb); } } @@ -1213,9 +1216,8 @@ 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("\\ADDR", children[0]->genWidthRTLIL(addr_bits)); @@ -1234,28 +1236,30 @@ 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); - 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->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); } @@ -1263,19 +1267,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) { @@ -1293,15 +1300,23 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // add entries to current_module->connections for assignments (outside of always blocks) case AST_ASSIGN: { - if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_AUTOWIRE) { - RTLIL::SigSpec right = children[1]->genRTLIL(); - RTLIL::SigSpec left = children[0]->genWidthRTLIL(right.size()); - current_module->connect(RTLIL::SigSig(left, right)); - } else { - RTLIL::SigSpec left = children[0]->genRTLIL(); - RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size()); - current_module->connect(RTLIL::SigSig(left, right)); + RTLIL::SigSpec left = children[0]->genRTLIL(); + RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size()); + if (left.has_const()) { + RTLIL::SigSpec new_left, new_right; + for (int i = 0; i < GetSize(left); i++) + if (left[i].wire) { + new_left.append(left[i]); + new_right.append(right[i]); + } + log_warning("Ignoring assignment to constant bits at %s:%d:\n" + " old assignment: %s = %s\n new assignment: %s = %s.\n", + filename.c_str(), linenum, log_signal(left), log_signal(right), + log_signal(new_left), log_signal(new_right)); + left = new_left; + right = new_right; } + current_module->connect(RTLIL::SigSig(left, right)); } break; @@ -1326,16 +1341,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) { @@ -1391,9 +1409,9 @@ 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) // note that this is using some global variables to communicate this special settings to AstNode::genRTLIL(). -RTLIL::SigSpec AstNode::genWidthRTLIL(int width, const std::map *new_subst_ptr) +RTLIL::SigSpec AstNode::genWidthRTLIL(int width, const dict *new_subst_ptr) { - const std::map *backup_subst_ptr = genRTLIL_subst_ptr; + const dict *backup_subst_ptr = genRTLIL_subst_ptr; if (new_subst_ptr) genRTLIL_subst_ptr = new_subst_ptr; diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 969cc230..a65d2dbb 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -28,10 +28,12 @@ #include "kernel/log.h" #include "libs/sha1/sha1.h" +#include "frontends/verilog/verilog_frontend.h" #include "ast.h" #include #include +#include #include YOSYS_NAMESPACE_BEGIN @@ -47,39 +49,55 @@ 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 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; #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) { log_assert(type == AST_MODULE); + last_blocking_assignment_warn = pair(); + 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")) { - std::map> mem2reg_places; - std::map mem2reg_candidates, dummy_proc_flags; + dict> mem2reg_places; + dict mem2reg_candidates, dummy_proc_flags; uint32_t flags = flag_mem2reg ? AstNode::MEM2REG_FL_ALL : 0; mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, dummy_proc_flags, flags); - std::set mem2reg_set; + pool mem2reg_set; for (auto &it : mem2reg_candidates) { 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; @@ -89,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) @@ -100,13 +118,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, verbose_activate: if (mem2reg_set.count(mem) == 0) { - log("Warning: Replacing memory %s with list of registers.", mem->str.c_str()); + std::string message = stringf("Replacing memory %s with list of registers.", mem->str.c_str()); bool first_element = true; for (auto &place : mem2reg_places[it.first]) { - log("%s%s", first_element ? " See " : ", ", place.c_str()); + message += stringf("%s%s", first_element ? " See " : ", ", place.c_str()); first_element = false; } - log("\n"); + log_warning("%s\n", message.c_str()); } silent_activate: @@ -141,6 +159,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } while (simplify(const_fold, at_zero, in_lvalue, 2, width_hint, sign_hint, in_param)) { } + recursion_counter--; return false; } @@ -149,11 +168,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // 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) + 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 == "$stop" || str == "$finish")) { + if ((type == AST_FCALL || type == AST_TCALL) && (str == "$display" || str == "$strobe" || str == "$monitor" || str == "$time" || str == "$stop" || str == "$finish" || + 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(); } @@ -182,6 +205,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, AstNode *first_node = this_wire_scope[node->str]; if (!node->is_input && !node->is_output && node->is_reg && node->children.size() == 0) goto wires_are_compatible; + if (first_node->children.size() == 0 && node->children.size() == 1 && node->children[0]->type == AST_RANGE) { + AstNode *r = node->children[0]; + if (r->range_valid && r->range_left == 0 && r->range_right == 0) { + delete r; + node->children.pop_back(); + } + } if (first_node->children.size() != node->children.size()) goto wires_are_incompatible; for (size_t j = 0; j < node->children.size(); j++) { @@ -242,6 +272,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; @@ -382,6 +416,41 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } + if (const_fold && type == AST_CASE) + { + while (children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { } + if (children[0]->type == AST_CONSTANT && children[0]->bits_only_01()) { + std::vector new_children; + new_children.push_back(children[0]); + for (int i = 1; i < GetSize(children); i++) { + AstNode *child = children[i]; + log_assert(child->type == AST_COND); + for (auto v : child->children) { + if (v->type == AST_DEFAULT) + goto keep_const_cond; + if (v->type == AST_BLOCK) + continue; + while (v->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { } + if (v->type == AST_CONSTANT && v->bits_only_01()) { + if (v->bits == children[0]->bits) { + while (i+1 < GetSize(children)) + delete children[++i]; + goto keep_const_cond; + } + continue; + } + goto keep_const_cond; + } + if (0) + keep_const_cond: + new_children.push_back(child); + else + delete child; + } + new_children.swap(children); + } + } + // simplify all children first // (iterate by index as e.g. auto wires can add new children in the process) for (size_t i = 0; i < children.size(); i++) { @@ -446,6 +515,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) @@ -575,9 +645,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, { AstNode *index_expr = nullptr; - for (int i = 0; 2*i < SIZE(id2ast->multirange_dimensions); i++) + for (int i = 0; 2*i < GetSize(id2ast->multirange_dimensions); i++) { - if (SIZE(children[0]->children) < i) + if (GetSize(children[0]->children) < i) log_error("Insufficient number of array indices for %s at %s:%d.\n", log_id(str), filename.c_str(), linenum); AstNode *new_index_expr = children[0]->children[i]->children.at(0)->clone(); @@ -591,7 +661,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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 = SIZE(id2ast->multirange_dimensions)/1; i < SIZE(children[0]->children); i++) + for (int i = GetSize(id2ast->multirange_dimensions)/1; i < GetSize(children[0]->children); i++) children.push_back(children[0]->children[i]->clone()); delete children[0]; @@ -611,7 +681,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width = 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", + log_warning("converting real value %e to binary %s at %s:%d.\n", children[0]->realvalue, log_signal(constvalue), filename.c_str(), linenum); delete children[0]; children[0] = mkconst_bits(constvalue.bits, sign_hint); @@ -653,7 +723,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } if (current_scope.count(str) == 0) { - // log("Warning: Creating auto-wire `%s' in module `%s'.\n", str.c_str(), current_ast_mod->str.c_str()); + // log_warning("Creating auto-wire `%s' in module `%s'.\n", str.c_str(), current_ast_mod->str.c_str()); AstNode *auto_wire = new AstNode(AST_AUTOWIRE); auto_wire->str = str; current_ast_mod->children.push_back(auto_wire); @@ -1123,7 +1193,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); - result_width = abs(left_at_zero_ast->integer - right_at_zero_ast->integer) + 1; + result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; } did_something = true; newNode = new AstNode(AST_CASE, shift_expr); @@ -1141,7 +1211,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++); @@ -1185,7 +1255,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; @@ -1196,9 +1266,8 @@ 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; } @@ -1222,9 +1291,13 @@ skip_dynamic_range_lvalue_expansion:; sstr << "$memwr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++); std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN"; - if (type == AST_ASSIGN_EQ) - log("Warning: Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n", - filename.c_str(), linenum); + if (type == AST_ASSIGN_EQ) { + pair this_blocking_assignment_warn(filename, linenum); + if (this_blocking_assignment_warn != last_blocking_assignment_warn) + log_warning("Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n", + filename.c_str(), linenum); + last_blocking_assignment_warn = this_blocking_assignment_warn; + } int mem_width, mem_size, addr_bits; children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits); @@ -1241,11 +1314,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 x_bits_addr, x_bits_data, set_bits_en; for (int i = 0; i < addr_bits; i++) @@ -1261,13 +1337,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()); @@ -1282,15 +1362,16 @@ skip_dynamic_range_lvalue_expansion:; std::vector 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 { @@ -1305,16 +1386,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; @@ -1326,23 +1408,29 @@ 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)); 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; @@ -1366,7 +1454,7 @@ skip_dynamic_range_lvalue_expansion:; RTLIL::Const arg_value = buf->bitsAsConst(); if (arg_value.as_bool()) - arg_value = const_sub(arg_value, 1, false, false, SIZE(arg_value)); + arg_value = const_sub(arg_value, 1, false, false, GetSize(arg_value)); delete buf; uint32_t result = 0; @@ -1455,9 +1543,9 @@ skip_dynamic_range_lvalue_expansion:; rtype = RTLIL::unescape_id(dpi_decl->children.at(0)->str); fname = RTLIL::unescape_id(dpi_decl->children.at(1)->str); - for (int i = 2; i < SIZE(dpi_decl->children); i++) + for (int i = 2; i < GetSize(dpi_decl->children); i++) { - if (i-2 >= SIZE(children)) + if (i-2 >= GetSize(children)) log_error("Insufficient number of arguments in DPI function call at %s:%d.\n", filename.c_str(), linenum); argtypes.push_back(RTLIL::unescape_id(dpi_decl->children.at(i)->str)); @@ -1480,6 +1568,44 @@ skip_dynamic_range_lvalue_expansion:; log_error("Can't resolve function name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum); } if (type == AST_TCALL) { + if (str == "\\$readmemh" || str == "\\$readmemb") + { + if (GetSize(children) < 2 || GetSize(children) > 4) + log_error("System function %s got %d arguments, expected 2-4 at %s:%d.\n", + RTLIL::unescape_id(str).c_str(), int(children.size()), filename.c_str(), linenum); + + AstNode *node_filename = children[0]->clone(); + while (node_filename->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_filename->type != AST_CONSTANT) + log_error("Failed to evaluate system function `%s' with non-constant 1st argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + + AstNode *node_memory = children[1]->clone(); + while (node_memory->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_memory->type != AST_IDENTIFIER || node_memory->id2ast == nullptr || node_memory->id2ast->type != AST_MEMORY) + log_error("Failed to evaluate system function `%s' with non-memory 2nd argument at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + + int start_addr = -1, finish_addr = -1; + + if (GetSize(children) > 2) { + AstNode *node_addr = children[2]->clone(); + 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); + } + + if (GetSize(children) > 3) { + AstNode *node_addr = children[3]->clone(); + 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); + } + + newNode = readmem(str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr); + goto apply_newNode; + } + if (current_scope.count(str) == 0 || current_scope[str]->type != AST_TASK) log_error("Can't resolve task name `%s' at %s:%d.\n", str.c_str(), filename.c_str(), linenum); } @@ -1558,7 +1684,7 @@ skip_dynamic_range_lvalue_expansion:; celltype = RTLIL::escape_id(celltype); AstNode *cell = new AstNode(AST_CELL, new AstNode(AST_CELLTYPE)); - cell->str = prefix.substr(0, SIZE(prefix)-1); + cell->str = prefix.substr(0, GetSize(prefix)-1); cell->children[0]->str = celltype; for (auto attr : decl->attributes) @@ -1681,7 +1807,7 @@ skip_dynamic_range_lvalue_expansion:; bool param_upto = current_scope[str]->range_valid && current_scope[str]->range_swapped; int param_offset = current_scope[str]->range_valid ? current_scope[str]->range_right : 0; int param_width = current_scope[str]->range_valid ? current_scope[str]->range_left - current_scope[str]->range_right + 1 : - SIZE(current_scope[str]->children[0]->bits); + GetSize(current_scope[str]->children[0]->bits); int tmp_range_left = children[0]->range_left, tmp_range_right = children[0]->range_right; if (param_upto) { tmp_range_left = (param_width + 2*param_offset) - children[0]->range_right - 1; @@ -1843,37 +1969,6 @@ skip_dynamic_range_lvalue_expansion:; newNode->realvalue = -children[0]->asReal(sign_hint); } break; - case AST_CASE: - if (children[0]->type == AST_CONSTANT && children[0]->bits_only_01()) { - std::vector new_children; - new_children.push_back(children[0]); - for (int i = 1; i < SIZE(children); i++) { - AstNode *child = children[i]; - log_assert(child->type == AST_COND); - for (auto v : child->children) { - if (v->type == AST_DEFAULT) - goto keep_const_cond; - if (v->type == AST_BLOCK) - continue; - if (v->type == AST_CONSTANT && v->bits_only_01()) { - if (v->bits == children[0]->bits) { - while (i+1 < SIZE(children)) - delete children[++i]; - goto keep_const_cond; - } - continue; - } - goto keep_const_cond; - } - if (0) - keep_const_cond: - new_children.push_back(child); - else - delete child; - } - new_children.swap(children); - } - break; case AST_TERNARY: if (children[0]->isConst()) { @@ -1977,6 +2072,7 @@ apply_newNode: if (!did_something) basic_prep = true; + recursion_counter--; return did_something; } @@ -1988,6 +2084,83 @@ static void replace_result_wire_name_in_function(AstNode *node, std::string &fro node->str = to; } +// 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 *block = new AstNode(AST_BLOCK); + + std::ifstream f; + f.open(mem_filename.c_str()); + + if (f.fail()) + log_error("Can not open file `%s` for %s at %s:%d.\n", mem_filename.c_str(), str.c_str(), filename.c_str(), linenum); + + 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); + + if (start_addr < 0) + start_addr = range_min; + + if (finish_addr < 0) + finish_addr = range_max; + + bool in_comment = false; + int increment = start_addr <= finish_addr ? +1 : -1; + int cursor = start_addr; + + while (!f.eof()) + { + std::string line, token; + std::getline(f, line); + + for (int i = 0; i < GetSize(line); i++) { + if (in_comment && line.substr(i, 2) == "*/") { + line[i] = ' '; + line[i+1] = ' '; + in_comment = false; + continue; + } + if (!in_comment && line.substr(i, 2) == "/*") + in_comment = true; + if (in_comment) + line[i] = ' '; + } + + while (1) + { + token = next_token(line, " \t\r\n"); + if (token.empty() || token.substr(0, 2) == "//") + break; + + if (token[0] == '@') { + token = token.substr(1); + const char *nptr = token.c_str(); + char *endptr; + cursor = strtol(nptr, &endptr, 16); + if (!*nptr || *endptr) + log_error("Can not parse address `%s` for %s at %s:%d.\n", nptr, str.c_str(), filename.c_str(), linenum); + continue; + } + + AstNode *value = VERILOG_FRONTEND::const2ast((is_readmemh ? "'h" : "'b") + token); + + 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)) + break; + cursor += increment; + } + + if ((cursor == finish_addr) || (increment > 0 && cursor >= range_max) || (increment < 0 && cursor <= range_min)) + break; + } + + return block; +} + // annotate the names of all wires and other named objects in a generate block void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map &name_map) { @@ -2063,8 +2236,8 @@ void AstNode::replace_ids(const std::string &prefix, const std::map> &mem2reg_places, - std::map &mem2reg_candidates, AstNode *that) +static void mark_memories_assign_lhs_complex(dict> &mem2reg_places, + dict &mem2reg_candidates, AstNode *that) { for (auto &child : that->children) mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, child); @@ -2078,8 +2251,8 @@ static void mark_memories_assign_lhs_complex(std::map> &mem2reg_places, - std::map &mem2reg_candidates, std::map &proc_flags, uint32_t &flags) +void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg_places, + dict &mem2reg_candidates, dict &proc_flags, uint32_t &flags) { uint32_t children_flags = 0; int ignore_children_counter = 0; @@ -2141,7 +2314,7 @@ void AstNode::mem2reg_as_needed_pass1(std::map> if (type == AST_MODULE && get_bool_attribute("\\mem2reg")) children_flags |= AstNode::MEM2REG_FL_ALL; - std::map *proc_flags_p = NULL; + dict *proc_flags_p = NULL; if (type == AST_ALWAYS) { int count_edge_events = 0; @@ -2150,12 +2323,12 @@ void AstNode::mem2reg_as_needed_pass1(std::map> count_edge_events++; if (count_edge_events != 1) children_flags |= AstNode::MEM2REG_FL_ASYNC; - proc_flags_p = new std::map; + proc_flags_p = new dict; } if (type == AST_INITIAL) { children_flags |= AstNode::MEM2REG_FL_INIT; - proc_flags_p = new std::map; + proc_flags_p = new dict; } uint32_t backup_flags = flags; @@ -2173,20 +2346,33 @@ void AstNode::mem2reg_as_needed_pass1(std::map> flags &= ~children_flags | backup_flags; if (proc_flags_p) { +#ifndef NDEBUG for (auto it : *proc_flags_p) log_assert((it.second & ~0xff000000) == 0); +#endif delete proc_flags_p; } } +bool AstNode::mem2reg_check(pool &mem2reg_set) +{ + if (type != AST_IDENTIFIER || !id2ast || !mem2reg_set.count(id2ast)) + return false; + + if (children.empty() || children[0]->type != AST_RANGE || GetSize(children[0]->children) != 1) + log_error("Invalid array access at %s:%d.\n", filename.c_str(), linenum); + + return true; +} + // actually replace memories with registers -void AstNode::mem2reg_as_needed_pass2(std::set &mem2reg_set, AstNode *mod, AstNode *block) +void AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, AstNode *block) { if (type == AST_BLOCK) block = this; - if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && block != NULL && children[0]->id2ast && - mem2reg_set.count(children[0]->id2ast) > 0 && children[0]->children[0]->children[0]->type != AST_CONSTANT) + if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && block != NULL && + children[0]->mem2reg_check(mem2reg_set) && children[0]->children[0]->children[0]->type != AST_CONSTANT) { std::stringstream sstr; sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++); @@ -2242,7 +2428,7 @@ void AstNode::mem2reg_as_needed_pass2(std::set &mem2reg_set, AstNode * type = AST_ASSIGN_EQ; } - if (type == AST_IDENTIFIER && id2ast && mem2reg_set.count(id2ast) > 0) + if (mem2reg_check(mem2reg_set)) { AstNode *bit_part_sel = NULL; if (children.size() == 2) @@ -2446,7 +2632,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) } log_assert(block != NULL); - log_assert(variables.count(str)); + log_assert(variables.count(str) != 0); while (!block->children.empty()) { diff --git a/frontends/ilang/.gitignore b/frontends/ilang/.gitignore index 72b06b0b..43106a81 100644 --- a/frontends/ilang/.gitignore +++ b/frontends/ilang/.gitignore @@ -1,4 +1,4 @@ -lexer.cc -parser.output -parser.tab.cc -parser.tab.h +ilang_lexer.cc +ilang_parser.output +ilang_parser.tab.cc +ilang_parser.tab.h diff --git a/frontends/ilang/Makefile.inc b/frontends/ilang/Makefile.inc index e832cfed..c15e2cc4 100644 --- a/frontends/ilang/Makefile.inc +++ b/frontends/ilang/Makefile.inc @@ -1,18 +1,18 @@ -GENFILES += frontends/ilang/parser.tab.cc -GENFILES += frontends/ilang/parser.tab.h -GENFILES += frontends/ilang/parser.output -GENFILES += frontends/ilang/lexer.cc +GENFILES += frontends/ilang/ilang_parser.tab.cc +GENFILES += frontends/ilang/ilang_parser.tab.h +GENFILES += frontends/ilang/ilang_parser.output +GENFILES += frontends/ilang/ilang_lexer.cc -frontends/ilang/parser.tab.cc: frontends/ilang/parser.y - $(P) bison -d -r all -b frontends/ilang/parser frontends/ilang/parser.y - $(Q) mv frontends/ilang/parser.tab.c frontends/ilang/parser.tab.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) mv frontends/ilang/ilang_parser.tab.c frontends/ilang/ilang_parser.tab.cc -frontends/ilang/parser.tab.h: frontends/ilang/parser.tab.cc +frontends/ilang/ilang_parser.tab.h: frontends/ilang/ilang_parser.tab.cc -frontends/ilang/lexer.cc: frontends/ilang/lexer.l - $(P) flex -o frontends/ilang/lexer.cc frontends/ilang/lexer.l +frontends/ilang/ilang_lexer.cc: frontends/ilang/ilang_lexer.l + $(P) flex -o frontends/ilang/ilang_lexer.cc frontends/ilang/ilang_lexer.l -OBJS += frontends/ilang/parser.tab.o frontends/ilang/lexer.o +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 f6f926db..7a4687a3 100644 --- a/frontends/ilang/ilang_frontend.cc +++ b/frontends/ilang/ilang_frontend.cc @@ -26,13 +26,13 @@ #include "kernel/register.h" #include "kernel/log.h" -YOSYS_NAMESPACE_BEGIN - void rtlil_frontend_ilang_yyerror(char const *s) { - log_error("Parser error in line %d: %s\n", rtlil_frontend_ilang_yyget_lineno(), s); + YOSYS_NAMESPACE_PREFIX log_error("Parser error in line %d: %s\n", rtlil_frontend_ilang_yyget_lineno(), s); } +YOSYS_NAMESPACE_BEGIN + struct IlangFrontend : public Frontend { IlangFrontend() : Frontend("ilang", "read modules from ilang file") { } virtual void help() diff --git a/frontends/ilang/lexer.l b/frontends/ilang/ilang_lexer.l similarity index 97% rename from frontends/ilang/lexer.l rename to frontends/ilang/ilang_lexer.l index 4109cd4b..ace992fb 100644 --- a/frontends/ilang/lexer.l +++ b/frontends/ilang/ilang_lexer.l @@ -30,10 +30,12 @@ #endif #include "ilang_frontend.h" -#include "parser.tab.h" +#include "ilang_parser.tab.h" + +USING_YOSYS_NAMESPACE #define YY_INPUT(buf,result,max_size) \ - result = ILANG_FRONTEND::lexin->readsome(buf, max_size); + result = readsome(*ILANG_FRONTEND::lexin, buf, max_size) %} diff --git a/frontends/ilang/parser.y b/frontends/ilang/ilang_parser.y similarity index 98% rename from frontends/ilang/parser.y rename to frontends/ilang/ilang_parser.y index a5cc0689..4661d577 100644 --- a/frontends/ilang/parser.y +++ b/frontends/ilang/ilang_parser.y @@ -36,7 +36,7 @@ namespace ILANG_FRONTEND { RTLIL::Process *current_process; std::vector*> switch_stack; std::vector case_stack; - std::map attrbuf; + dict attrbuf; } using namespace ILANG_FRONTEND; YOSYS_NAMESPACE_END @@ -183,6 +183,9 @@ memory_options: memory_options TOK_SIZE TOK_INT { current_memory->size = $3; } | + memory_options TOK_OFFSET TOK_INT { + current_memory->start_offset = $3; + } | /* empty */; cell_stmt: diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index a9ab022a..464c5c94 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -22,7 +22,6 @@ #include "kernel/log.h" YOSYS_NAMESPACE_BEGIN -using namespace PASS_DFFLIBMAP; struct token_t { char type; diff --git a/frontends/verific/build_amd64.txt b/frontends/verific/build_amd64.txt index 94615d38..9bb6e320 100644 --- a/frontends/verific/build_amd64.txt +++ b/frontends/verific/build_amd64.txt @@ -10,6 +10,7 @@ CONFIG := clang ENABLE_TCL := 0 ENABLE_QT4 := 0 ENABLE_ABC := 0 +ENABLE_PLUGINS := 0 ENABLE_VERIFIC := 1 CXXFLAGS += -m32 LDFLAGS += -m32 diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index d0f14838..79abcf24 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -20,11 +20,14 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" #include "kernel/log.h" -#include #include #include #include -#include + +#ifndef _WIN32 +# include +# include +#endif USING_YOSYS_NAMESPACE @@ -324,7 +327,7 @@ static bool import_netlist_instance_cells(RTLIL::Module *module, std::mapGetCin()->IsGnd()) { module->addAdd(RTLIL::escape_id(inst->Name()), IN1, IN2, out, SIGNED); } else { - RTLIL::SigSpec tmp = module->addWire(NEW_ID, SIZE(out)); + RTLIL::SigSpec tmp = module->addWire(NEW_ID, GetSize(out)); module->addAdd(NEW_ID, IN1, IN2, tmp, SIGNED); module->addAdd(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetCin()), out, false); } @@ -687,8 +690,8 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::setparameters["\\CLK_ENABLE"] = false; cell->parameters["\\CLK_POLARITY"] = true; cell->parameters["\\TRANSPARENT"] = false; - cell->parameters["\\ABITS"] = SIZE(addr); - cell->parameters["\\WIDTH"] = SIZE(data); + cell->parameters["\\ABITS"] = GetSize(addr); + cell->parameters["\\WIDTH"] = GetSize(data); cell->setPort("\\CLK", RTLIL::State::S0); cell->setPort("\\ADDR", addr); cell->setPort("\\DATA", data); @@ -709,9 +712,9 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::setparameters["\\CLK_ENABLE"] = false; cell->parameters["\\CLK_POLARITY"] = true; cell->parameters["\\PRIORITY"] = 0; - cell->parameters["\\ABITS"] = SIZE(addr); - cell->parameters["\\WIDTH"] = SIZE(data); - cell->setPort("\\EN", RTLIL::SigSpec(net_map.at(inst->GetControl())).repeat(SIZE(data))); + cell->parameters["\\ABITS"] = GetSize(addr); + cell->parameters["\\WIDTH"] = GetSize(data); + cell->setPort("\\EN", RTLIL::SigSpec(net_map.at(inst->GetControl())).repeat(GetSize(data))); cell->setPort("\\CLK", RTLIL::State::S0); cell->setPort("\\ADDR", addr); cell->setPort("\\DATA", data); @@ -727,7 +730,7 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::setIsOperator()) - log("Warning: Unsupported Verific operator: %s (fallback to gate level implementation provided by verific)\n", inst->View()->Owner()->Name()); + log_warning("Unsupported Verific operator: %s (fallback to gate level implementation provided by verific)\n", inst->View()->Owner()->Name()); } else { if (import_netlist_instance_gates(module, net_map, inst)) continue; @@ -753,9 +756,9 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::sethasPort(RTLIL::escape_id(port_name))) conn = cell->getPort(RTLIL::escape_id(port_name)); - while (SIZE(conn) <= port_offset) { + while (GetSize(conn) <= port_offset) { if (pr->GetPort()->GetDir() != DIR_IN) - conn.append(module->addWire(NEW_ID, port_offset - SIZE(conn))); + conn.append(module->addWire(NEW_ID, port_offset - GetSize(conn))); conn.append(RTLIL::State::Sz); } conn.replace(port_offset, net_map.at(pr->GetNet())); diff --git a/frontends/verilog/.gitignore b/frontends/verilog/.gitignore index 72b06b0b..1d4ae9e5 100644 --- a/frontends/verilog/.gitignore +++ b/frontends/verilog/.gitignore @@ -1,4 +1,4 @@ -lexer.cc -parser.output -parser.tab.cc -parser.tab.h +verilog_lexer.cc +verilog_parser.output +verilog_parser.tab.cc +verilog_parser.tab.h diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc index 49eb320e..92cbd0b8 100644 --- a/frontends/verilog/Makefile.inc +++ b/frontends/verilog/Makefile.inc @@ -1,20 +1,20 @@ -GENFILES += frontends/verilog/parser.tab.cc -GENFILES += frontends/verilog/parser.tab.h -GENFILES += frontends/verilog/parser.output -GENFILES += frontends/verilog/lexer.cc +GENFILES += frontends/verilog/verilog_parser.tab.cc +GENFILES += frontends/verilog/verilog_parser.tab.h +GENFILES += frontends/verilog/verilog_parser.output +GENFILES += frontends/verilog/verilog_lexer.cc -frontends/verilog/parser.tab.cc: frontends/verilog/parser.y - $(P) bison -d -r all -b frontends/verilog/parser frontends/verilog/parser.y - $(Q) mv frontends/verilog/parser.tab.c frontends/verilog/parser.tab.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) mv frontends/verilog/verilog_parser.tab.c frontends/verilog/verilog_parser.tab.cc -frontends/verilog/parser.tab.h: frontends/verilog/parser.tab.cc +frontends/verilog/verilog_parser.tab.h: frontends/verilog/verilog_parser.tab.cc -frontends/verilog/lexer.cc: frontends/verilog/lexer.l - $(P) flex -o frontends/verilog/lexer.cc frontends/verilog/lexer.l +frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l + $(P) flex -o frontends/verilog/verilog_lexer.cc frontends/verilog/verilog_lexer.l -OBJS += frontends/verilog/parser.tab.o -OBJS += frontends/verilog/lexer.o +OBJS += frontends/verilog/verilog_parser.tab.o +OBJS += frontends/verilog/verilog_lexer.o OBJS += frontends/verilog/preproc.o OBJS += frontends/verilog/verilog_frontend.o OBJS += frontends/verilog/const2ast.o diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc index a81e3010..735bc5f9 100644 --- a/frontends/verilog/const2ast.cc +++ b/frontends/verilog/const2ast.cc @@ -132,8 +132,16 @@ static void my_strtobin(std::vector &data, const char *str, int le } // convert the verilog code for a constant to an AST node -AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type) +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()); + return ret; + } + const char *str = code.c_str(); // Strings @@ -174,7 +182,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type) if (str == endptr) len_in_bits = -1; - // The "'s?[bodh]" syntax + // The "'s?[bodhBODH]" syntax if (*endptr == '\'') { std::vector data; @@ -186,15 +194,19 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type) switch (*(endptr+1)) { case 'b': + case 'B': my_strtobin(data, endptr+2, len_in_bits, 2, case_type); break; case 'o': + case 'O': my_strtobin(data, endptr+2, len_in_bits, 8, case_type); break; case 'd': + case 'D': my_strtobin(data, endptr+2, len_in_bits, 10, case_type); break; case 'h': + case 'H': my_strtobin(data, endptr+2, len_in_bits, 16, case_type); break; default: diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index f8343321..e2118630 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -201,8 +201,8 @@ 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"); - while ((rc = f.readsome(buffer, sizeof(buffer)-1)) > 0) { + 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); } @@ -221,7 +221,8 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons input_buffer_charp = 0; input_file(f, filename); - defines_map["__YOSYS__"] = "1"; + defines_map["YOSYS"] = "1"; + defines_map["SYNTHESIS"] = "1"; while (!input_buffer.empty()) { @@ -422,7 +423,7 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons if (tok == "(" || tok == "{" || tok == "[") level++; } - for (size_t i = 0; i < args.size(); i++) + for (int i = 0; i < GetSize(args); i++) defines_map[stringf("macro_%s_arg%d", name.c_str(), i+1)] = args[i]; } else { insert_input(tok); diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index c6d4a0b7..635c9ce4 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -54,6 +54,10 @@ struct VerilogFrontend : public Frontend { 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() statements\n"); + log(" (assert support is also enabled with -sv)\n"); + log("\n"); log(" -dump_ast1\n"); log(" dump abstract syntax tree (before simplification)\n"); log("\n"); @@ -83,11 +87,20 @@ 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("\n"); @@ -139,6 +152,7 @@ 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; @@ -154,6 +168,7 @@ struct VerilogFrontend : public Frontend { frontend_verilog_yydebug = false; sv_mode = false; + formal_mode = false; log_header("Executing Verilog-2005 frontend.\n"); @@ -166,6 +181,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 +205,10 @@ struct VerilogFrontend : public Frontend { flag_nolatches = true; continue; } + if (arg == "-nomeminit") { + flag_nomeminit = true; + continue; + } if (arg == "-nomem2reg") { flag_nomem2reg = true; continue; @@ -257,7 +280,8 @@ struct VerilogFrontend : public Frontend { } extra_args(f, filename, args, argidx); - log("Parsing Verilog input from `%s' to AST representation.\n", filename.c_str()); + log("Parsing %s%s input from `%s' to AST representation.\n", + formal_mode ? "formal " : "", sv_mode ? "SystemVerilog" : "Verilog", filename.c_str()); AST::current_filename = filename; AST::set_line_num = &frontend_verilog_yyset_lineno; @@ -288,7 +312,7 @@ 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); + 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; @@ -300,22 +324,6 @@ struct VerilogFrontend : public Frontend { } } VerilogFrontend; -// the yyerror function used by bison to report parser errors -void frontend_verilog_yyerror(char const *fmt, ...) -{ - va_list ap; - char buffer[1024]; - char *p = buffer; - p += snprintf(p, buffer + sizeof(buffer) - p, "Parser error in line %s:%d: ", - AST::current_filename.c_str(), frontend_verilog_yyget_lineno()); - va_start(ap, fmt); - p += vsnprintf(p, buffer + sizeof(buffer) - p, fmt, ap); - va_end(ap); - p += snprintf(p, buffer + sizeof(buffer) - p, "\n"); - log_error("%s", buffer); - exit(1); -} - struct VerilogDefaults : public Pass { VerilogDefaults() : Pass("verilog_defaults", "set default options for read_verilog") { } virtual void help() @@ -376,3 +384,19 @@ struct VerilogDefaults : public Pass { YOSYS_NAMESPACE_END +// the yyerror function used by bison to report parser errors +void frontend_verilog_yyerror(char const *fmt, ...) +{ + va_list ap; + char buffer[1024]; + char *p = buffer; + p += snprintf(p, buffer + sizeof(buffer) - p, "Parser error in line %s:%d: ", + YOSYS_NAMESPACE_PREFIX AST::current_filename.c_str(), frontend_verilog_yyget_lineno()); + va_start(ap, fmt); + p += vsnprintf(p, buffer + sizeof(buffer) - p, fmt, ap); + va_end(ap); + p += snprintf(p, buffer + sizeof(buffer) - p, "\n"); + YOSYS_NAMESPACE_PREFIX log_error("%s", buffer); + exit(1); +} + diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h index af6495f8..5561f54c 100644 --- a/frontends/verilog/verilog_frontend.h +++ b/frontends/verilog/verilog_frontend.h @@ -43,7 +43,7 @@ namespace VERILOG_FRONTEND extern struct AST::AstNode *current_ast; // this function converts a Verilog constant to an AST_CONSTANT node - AST::AstNode *const2ast(std::string code, char case_type = 0); + AST::AstNode *const2ast(std::string code, char case_type = 0, bool warn_z = false); // state of `default_nettype extern bool default_nettype_wire; @@ -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/lexer.l b/frontends/verilog/verilog_lexer.l similarity index 83% rename from frontends/verilog/lexer.l rename to frontends/verilog/verilog_lexer.l index c9302aba..8fbaa953 100644 --- a/frontends/verilog/lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -42,7 +42,7 @@ #include "kernel/log.h" #include "verilog_frontend.h" #include "frontends/ast/ast.h" -#include "parser.tab.h" +#include "verilog_parser.tab.h" USING_YOSYS_NAMESPACE using namespace AST; @@ -64,7 +64,7 @@ YOSYS_NAMESPACE_END return TOK_ID; #define YY_INPUT(buf,result,max_size) \ - result = lexin->readsome(buf, max_size); + result = readsome(*VERILOG_FRONTEND::lexin, buf, max_size) %} @@ -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++; @@ -162,8 +169,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; return TOK_ID; } +"property" { if (formal_mode) return TOK_PROPERTY; SV_KEYWORD(TOK_PROPERTY); } "logic" { SV_KEYWORD(TOK_REG); } "bit" { SV_KEYWORD(TOK_REG); } @@ -177,12 +185,12 @@ YOSYS_NAMESPACE_END "genvar" { return TOK_GENVAR; } "real" { return TOK_REAL; } -[0-9]+ { +[0-9][0-9_]* { frontend_verilog_yylval.string = new std::string(yytext); return TOK_CONST; } -[0-9]*[ \t]*\'s?[bodh][ \t\r\n]*[0-9a-fA-FzxZX?_]+ { +[0-9]*[ \t]*\'s?[bodhBODH][ \t\r\n]*[0-9a-fA-FzxZX?_]+ { frontend_verilog_yylval.string = new std::string(yytext); return TOK_CONST; } @@ -240,7 +248,7 @@ and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1 { supply0 { return TOK_SUPPLY0; } supply1 { return TOK_SUPPLY1; } -"$"(display|time|stop|finish) { +"$"(display|strobe|monitor|time|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) { frontend_verilog_yylval.string = new std::string(yytext); return TOK_ID; } @@ -254,8 +262,12 @@ supply1 { return TOK_SUPPLY1; } } "/*"[ \t]*(synopsys|synthesis)[ \t]*translate_off[ \t]*"*/" { - log("Warning: Found one of those horrible `(synopsys|synthesis) translate_off' comments.\n"); - log("It is strongly suggested to use `ifdef constructs instead!\n"); + static bool printed_warning = false; + if (!printed_warning) { + log_warning("Found one of those horrible `(synopsys|synthesis) translate_off' comments.\n" + "Yosys does support them but it is recommended to use `ifdef constructs instead!\n"); + printed_warning = true; + } BEGIN(SYNOPSYS_TRANSLATE_OFF); } . /* ignore synopsys translate_off body */ @@ -266,13 +278,21 @@ supply1 { return TOK_SUPPLY1; } BEGIN(SYNOPSYS_FLAGS); } full_case { - log("Warning: Found one of those horrible `(synopsys|synthesis) full_case' comments.\n"); - log("It is strongly suggested to use verilog x-values and default branches instead!\n"); + 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"); + printed_warning = true; + } return TOK_SYNOPSYS_FULL_CASE; } parallel_case { - log("Warning: Found one of those horrible `(synopsys|synthesis) parallel_case' comments.\n"); - log("It is strongly suggested to use verilog `parallel_case' attributes instead!\n"); + 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"); + printed_warning = true; + } return TOK_SYNOPSYS_PARALLEL_CASE; } . /* ignore everything else */ @@ -342,7 +362,10 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { [ \t\r\n] /* ignore whitespaces */ \\[\r\n] /* ignore continuation sequence */ "//"[^\r\n]* /* ignore one-line comments */ -"#"[$a-zA-Z_0-9\.]+ /* ignore simulation timings */ + +"#"\ *[0-9][0-9_]* /* ignore simulation timings */ +"#"\ *[0-9][0-9_]*\.[0-9][0-9_]* /* ignore simulation timings */ +"#"\ *[$a-zA-Z_\.][$a-zA-Z_0-9\.]* /* ignore simulation timings */ . { return *yytext; } diff --git a/frontends/verilog/parser.y b/frontends/verilog/verilog_parser.y similarity index 92% rename from frontends/verilog/parser.y rename to frontends/verilog/verilog_parser.y index a9f69a49..d935cab3 100644 --- a/frontends/verilog/parser.y +++ b/frontends/verilog/verilog_parser.y @@ -57,7 +57,7 @@ namespace VERILOG_FRONTEND { std::vector 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 @@ -111,7 +111,7 @@ static void free_attr(std::map *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 range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int %type wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list @@ -139,10 +139,11 @@ static void free_attr(std::map *al) %% input: { + ast_stack.clear(); ast_stack.push_back(current_ast); } design { ast_stack.pop_back(); - log_assert(SIZE(ast_stack) == 0); + log_assert(GetSize(ast_stack) == 0); for (auto &it : default_attr_list) delete it.second; default_attr_list.clear(); @@ -240,7 +241,7 @@ module: }; module_para_opt: - '#' '(' module_para_list ')' | /* empty */; + '#' '(' { astbuf1 = nullptr; } module_para_list { if (astbuf1) delete astbuf1; } ')' | /* empty */; module_para_list: single_module_para | @@ -249,11 +250,10 @@ module_para_list: single_module_para: TOK_PARAMETER { + if (astbuf1) delete astbuf1; astbuf1 = new AstNode(AST_PARAMETER); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); - } param_signed param_integer param_range single_param_decl { - delete astbuf1; - }; + } param_signed param_integer param_range single_param_decl | single_param_decl; module_args_opt: '(' ')' | /* empty */ | '(' module_args optional_comma ')'; @@ -310,10 +310,17 @@ module_arg: do_not_require_port_stubs = true; }; +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; }; @@ -449,7 +456,7 @@ task_func_decl: } opt_dpi_function_args ';' { current_function_or_task = NULL; } | - attr TOK_TASK TOK_ID ';' { + attr TOK_TASK TOK_ID { current_function_or_task = new AstNode(AST_TASK); current_function_or_task->str = *$3; append_attr(current_function_or_task, $1); @@ -457,11 +464,11 @@ task_func_decl: ast_stack.push_back(current_function_or_task); current_function_or_task_port_id = 1; delete $3; - } task_func_body TOK_ENDTASK { + } task_func_args_opt ';' task_func_body TOK_ENDTASK { current_function_or_task = NULL; ast_stack.pop_back(); } | - attr TOK_FUNCTION opt_signed range_or_signed_int TOK_ID ';' { + attr TOK_FUNCTION opt_signed range_or_signed_int TOK_ID { current_function_or_task = new AstNode(AST_FUNCTION); current_function_or_task->str = *$5; append_attr(current_function_or_task, $1); @@ -478,7 +485,7 @@ task_func_decl: current_function_or_task->children.push_back(outreg); current_function_or_task_port_id = 1; delete $5; - } task_func_body TOK_ENDFUNCTION { + } task_func_args_opt ';' task_func_body TOK_ENDFUNCTION { current_function_or_task = NULL; ast_stack.pop_back(); }; @@ -512,6 +519,45 @@ opt_signed: $$ = false; }; +task_func_args_opt: + '(' ')' | /* empty */ | '(' { + albuf = nullptr; + astbuf1 = nullptr; + astbuf2 = nullptr; + } task_func_args optional_comma { + delete astbuf1; + if (astbuf2 != NULL) + delete astbuf2; + free_attr(albuf); + } ')'; + +task_func_args: + task_func_port | task_func_args ',' task_func_port; + +task_func_port: + attr wire_type range { + if (albuf) { + delete astbuf1; + if (astbuf2 != NULL) + delete astbuf2; + free_attr(albuf); + } + albuf = $1; + astbuf1 = $2; + astbuf2 = $3; + if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) { + if (astbuf2) { + frontend_verilog_yyerror("Syntax error."); + } else { + astbuf2 = new AstNode(AST_RANGE); + astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true)); + astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_right, true)); + } + } + if (astbuf2 && astbuf2->children.size() != 2) + frontend_verilog_yyerror("Syntax error."); + } wire_name | wire_name; + task_func_body: task_func_body behavioral_stmt | /* empty */; @@ -568,6 +614,8 @@ param_decl_list: single_param_decl: TOK_ID '=' expr { + if (astbuf1 == nullptr) + frontend_verilog_yyerror("syntax error"); AstNode *node = astbuf1->clone(); node->str = *$1; delete node->children[0]; @@ -609,27 +657,39 @@ wire_decl: } if (astbuf2 && astbuf2->children.size() != 2) frontend_verilog_yyerror("Syntax error."); - } wire_name_list ';' { + } wire_name_list { delete astbuf1; if (astbuf2 != NULL) delete astbuf2; free_attr(albuf); - } | - attr TOK_SUPPLY0 TOK_ID ';' { + } ';' | + attr TOK_SUPPLY0 TOK_ID { ast_stack.back()->children.push_back(new AstNode(AST_WIRE)); ast_stack.back()->children.back()->str = *$3; append_attr(ast_stack.back()->children.back(), $1); ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1))); ast_stack.back()->children.back()->children[0]->str = *$3; delete $3; - } | - attr TOK_SUPPLY1 TOK_ID ';' { + } opt_supply_wires ';' | + attr TOK_SUPPLY1 TOK_ID { ast_stack.back()->children.push_back(new AstNode(AST_WIRE)); ast_stack.back()->children.back()->str = *$3; append_attr(ast_stack.back()->children.back(), $1); ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(1, false, 1))); ast_stack.back()->children.back()->children[0]->str = *$3; delete $3; + } opt_supply_wires ';'; + +opt_supply_wires: + /* empty */ | + opt_supply_wires ',' TOK_ID { + AstNode *wire_node = ast_stack.back()->children.at(GetSize(ast_stack.back()->children)-2)->clone(); + AstNode *assign_node = ast_stack.back()->children.at(GetSize(ast_stack.back()->children)-1)->clone(); + wire_node->str = *$3; + assign_node->children[0]->str = *$3; + ast_stack.back()->children.push_back(wire_node); + ast_stack.back()->children.push_back(assign_node); + delete $3; }; wire_name_list: @@ -689,7 +749,7 @@ 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; @@ -709,7 +769,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); @@ -874,27 +934,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 ';' | + non_opt_delay behavioral_stmt | + simple_behavioral_stmt ';' | ';' | hierarchical_id attr { AstNode *node = new AstNode(AST_TCALL); node->str = *$1; @@ -1008,10 +1075,6 @@ opt_synopsys_attr: } | /* empty */; -behavioral_stmt_opt: - behavioral_stmt | - ';' ; - behavioral_stmt_list: behavioral_stmt_list behavioral_stmt | /* empty */; @@ -1040,7 +1103,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(); @@ -1211,7 +1274,7 @@ basic_expr: if ($4->substr(0, 1) != "'") frontend_verilog_yyerror("Syntax error."); AstNode *bits = $2; - AstNode *val = const2ast(*$4, case_type_stack.size() == 0 ? 0 : case_type_stack.back()); + AstNode *val = const2ast(*$4, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), true); if (val == NULL) log_error("Value conversion failed: `%s'\n", $4->c_str()); $$ = new AstNode(AST_TO_BITS, bits, val); @@ -1222,7 +1285,7 @@ basic_expr: frontend_verilog_yyerror("Syntax error."); AstNode *bits = new AstNode(AST_IDENTIFIER); bits->str = *$1; - AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back()); + AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), true); if (val == NULL) log_error("Value conversion failed: `%s'\n", $2->c_str()); $$ = new AstNode(AST_TO_BITS, bits, val); @@ -1230,14 +1293,14 @@ basic_expr: delete $2; } | TOK_CONST TOK_CONST { - $$ = const2ast(*$1 + *$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back()); + $$ = const2ast(*$1 + *$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), true); if ($$ == NULL || (*$2)[0] != '\'') log_error("Value conversion failed: `%s%s'\n", $1->c_str(), $2->c_str()); delete $1; delete $2; } | TOK_CONST { - $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back()); + $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), true); if ($$ == NULL) log_error("Value conversion failed: `%s'\n", $1->c_str()); delete $1; @@ -1278,10 +1341,15 @@ basic_expr: '(' expr ')' { $$ = $2; } | + '(' expr ':' expr ':' expr ')' { + delete $2; + $$ = $4; + delete $6; + } | '{' concat_list '}' { $$ = $2; } | - '{' expr '{' expr '}' '}' { + '{' expr '{' concat_list '}' '}' { $$ = new AstNode(AST_REPLICATE, $2, $4); } | '~' attr basic_expr %prec UNARY_OPS { diff --git a/frontends/vhdl2verilog/vhdl2verilog.cc b/frontends/vhdl2verilog/vhdl2verilog.cc index b408d621..82ff7b50 100644 --- a/frontends/vhdl2verilog/vhdl2verilog.cc +++ b/frontends/vhdl2verilog/vhdl2verilog.cc @@ -20,14 +20,17 @@ #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/log.h" -#include #include #include #include -#include #include #include +#ifndef _WIN32 +# include +# include +#endif + YOSYS_NAMESPACE_BEGIN struct Vhdl2verilogPass : public Pass { @@ -120,11 +123,8 @@ struct Vhdl2verilogPass : public Pass { if (top_entity.empty()) log_cmd_error("Missing -top option.\n"); - char tempdir_name[] = "/tmp/yosys-vhdl2verilog-XXXXXX"; - char *p = mkdtemp(tempdir_name); - log("Using temp directory %s.\n", tempdir_name); - if (p == NULL) - log_error("For some reason mkdtemp() failed!\n"); + std::string tempdir_name = make_temp_dir("/tmp/yosys-vhdl2verilog-XXXXXX"); + log("Using temp directory %s.\n", tempdir_name.c_str()); if (!out_file.empty() && out_file[0] != '/') { char pwd[PATH_MAX]; @@ -135,7 +135,7 @@ struct Vhdl2verilogPass : public Pass { out_file = pwd + ("/" + out_file); } - FILE *f = fopen(stringf("%s/files.list", tempdir_name).c_str(), "wt"); + FILE *f = fopen(stringf("%s/files.list", tempdir_name.c_str()).c_str(), "wt"); while (argidx < args.size()) { std::string file = args[argidx++]; if (file.empty()) @@ -156,38 +156,25 @@ struct Vhdl2verilogPass : public Pass { std::string command = "exec 2>&1; "; if (!vhdl2verilog_dir.empty()) command += stringf("cd '%s'; . ./setup_env.sh; ", vhdl2verilog_dir.c_str()); - command += stringf("cd '%s'; vhdl2verilog -out '%s' -filelist files.list -top '%s'%s", tempdir_name, + command += stringf("cd '%s'; vhdl2verilog -out '%s' -filelist files.list -top '%s'%s", tempdir_name.c_str(), out_file.empty() ? "vhdl2verilog_output.v" : out_file.c_str(), top_entity.c_str(), extra_opts.c_str()); log("Running '%s'..\n", command.c_str()); - errno = ENOMEM; // popen does not set errno if memory allocation fails, therefore set it by hand - f = popen(command.c_str(), "r"); - if (f == NULL) - log_error("Opening pipe to `%s' for reading failed: %s\n", command.c_str(), strerror(errno)); - - char logbuf[1024]; - while (fgets(logbuf, 1024, f) != NULL) - log("%s", logbuf); - - int ret = pclose(f); - if (ret < 0) - log_error("Closing pipe to `%s' failed: %s\n", command.c_str(), strerror(errno)); - if (WEXITSTATUS(ret) != 0) - log_error("Execution of command \"%s\" failed: the shell returned %d\n", command.c_str(), WEXITSTATUS(ret)); + int ret = run_command(command, [](const std::string &line) { log("%s", line.c_str()); }); + if (ret != 0) + log_error("Execution of command \"%s\" failed: return code %d.\n", command.c_str(), ret); if (out_file.empty()) { std::ifstream ff; - ff.open(stringf("%s/vhdl2verilog_output.v", tempdir_name).c_str()); + ff.open(stringf("%s/vhdl2verilog_output.v", tempdir_name.c_str()).c_str()); if (ff.fail()) log_error("Can't open vhdl2verilog output file `vhdl2verilog_output.v'.\n"); - Frontend::frontend_call(design, &ff, stringf("%s/vhdl2verilog_output.v", tempdir_name), "verilog"); + Frontend::frontend_call(design, &ff, stringf("%s/vhdl2verilog_output.v", tempdir_name.c_str()), "verilog"); } - log_header("Removing temp directory `%s':\n", tempdir_name); - if (system(stringf("rm -rf '%s'", tempdir_name).c_str()) != 0) - log_error("Execution of \"rm -rf '%s'\" failed!\n", tempdir_name); - + log_header("Removing temp directory `%s':\n", tempdir_name.c_str()); + remove_directory(tempdir_name); log_pop(); } } Vhdl2verilogPass; diff --git a/kernel/bitpattern.h b/kernel/bitpattern.h index 91f54593..00bbc3bf 100644 --- a/kernel/bitpattern.h +++ b/kernel/bitpattern.h @@ -23,24 +23,46 @@ #include "kernel/log.h" #include "kernel/rtlil.h" +YOSYS_NAMESPACE_BEGIN + struct BitPatternPool { int width; - typedef std::vector bits_t; - std::set pool; + struct bits_t { + std::vector bitdata; + unsigned int cached_hash; + bits_t(int width = 0) : bitdata(width), cached_hash(0) { } + RTLIL::State &operator[](int index) { + return bitdata[index]; + } + const RTLIL::State &operator[](int index) const { + return bitdata[index]; + } + bool operator==(const bits_t &other) const { + if (hash() != other.hash()) + return false; + return bitdata == other.bitdata; + } + unsigned int hash() const { + if (!cached_hash) + ((bits_t*)this)->cached_hash = hash_ops>::hash(bitdata); + return cached_hash; + } + }; + pool database; BitPatternPool(RTLIL::SigSpec sig) { width = sig.size(); if (width > 0) { - std::vector pattern(width); + bits_t pattern(width); for (int i = 0; i < width; i++) { if (sig[i].wire == NULL && sig[i].data <= RTLIL::State::S1) pattern[i] = sig[i].data; else pattern[i] = RTLIL::State::Sa; } - pool.insert(pattern); + database.insert(pattern); } } @@ -48,17 +70,18 @@ struct BitPatternPool { this->width = width; if (width > 0) { - std::vector pattern(width); + bits_t pattern(width); for (int i = 0; i < width; i++) pattern[i] = RTLIL::State::Sa; - pool.insert(pattern); + database.insert(pattern); } } bits_t sig2bits(RTLIL::SigSpec sig) { - bits_t bits = sig.as_const().bits; - for (auto &b : bits) + bits_t bits; + bits.bitdata = sig.as_const().bits; + for (auto &b : bits.bitdata) if (b > RTLIL::State::S1) b = RTLIL::State::Sa; return bits; @@ -66,8 +89,8 @@ struct BitPatternPool bool match(bits_t a, bits_t b) { - log_assert(int(a.size()) == width); - log_assert(int(b.size()) == width); + log_assert(int(a.bitdata.size()) == width); + log_assert(int(b.bitdata.size()) == width); for (int i = 0; i < width; i++) if (a[i] <= RTLIL::State::S1 && b[i] <= RTLIL::State::S1 && a[i] != b[i]) return false; @@ -77,7 +100,7 @@ struct BitPatternPool bool has_any(RTLIL::SigSpec sig) { bits_t bits = sig2bits(sig); - for (auto &it : pool) + for (auto &it : database) if (match(it, bits)) return true; return false; @@ -86,13 +109,13 @@ struct BitPatternPool bool has_all(RTLIL::SigSpec sig) { bits_t bits = sig2bits(sig); - for (auto &it : pool) + for (auto &it : database) if (match(it, bits)) { for (int i = 0; i < width; i++) if (bits[i] > RTLIL::State::S1 && it[i] <= RTLIL::State::S1) - goto next_pool_entry; + goto next_database_entry; return true; - next_pool_entry:; + next_database_entry:; } return false; } @@ -101,36 +124,38 @@ struct BitPatternPool { bool status = false; bits_t bits = sig2bits(sig); - std::vector pattern_list; - for (auto &it : pool) - if (match(it, bits)) - pattern_list.push_back(it); - for (auto pattern : pattern_list) { - pool.erase(pattern); - for (int i = 0; i < width; i++) { - if (pattern[i] != RTLIL::State::Sa || bits[i] == RTLIL::State::Sa) - continue; - bits_t new_pattern = pattern; - new_pattern[i] = bits[i] == RTLIL::State::S1 ? RTLIL::State::S0 : RTLIL::State::S1; - pool.insert(new_pattern); - } - status = true; - } + for (auto it = database.begin(); it != database.end();) + if (match(*it, bits)) { + for (int i = 0; i < width; i++) { + if ((*it)[i] != RTLIL::State::Sa || bits[i] == RTLIL::State::Sa) + continue; + bits_t new_pattern; + new_pattern.bitdata = it->bitdata; + new_pattern[i] = bits[i] == RTLIL::State::S1 ? RTLIL::State::S0 : RTLIL::State::S1; + database.insert(new_pattern); + } + it = database.erase(it); + status = true; + continue; + } else + ++it; return status; } bool take_all() { - if (pool.empty()) + if (database.empty()) return false; - pool.clear(); + database.clear(); return true; } bool empty() { - return pool.empty(); + return database.empty(); } }; +YOSYS_NAMESPACE_END + #endif diff --git a/kernel/calc.cc b/kernel/calc.cc index 41179d04..aa3e8b91 100644 --- a/kernel/calc.cc +++ b/kernel/calc.cc @@ -303,7 +303,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, SIZE(arg1)), signed1); + extend_u0(arg1_ext, std::max(result_len, GetSize(arg1)), signed1); return const_shift_worker(arg1_ext, arg2, false, +1, result_len); } diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 85c21ef3..533c370f 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -27,13 +27,13 @@ YOSYS_NAMESPACE_BEGIN struct CellType { RTLIL::IdString type; - std::set inputs, outputs; + pool inputs, outputs; bool is_evaluable; }; struct CellTypes { - std::map cell_types; + dict cell_types; CellTypes() { @@ -55,7 +55,7 @@ struct CellTypes setup_stdcells_mem(); } - void setup_type(RTLIL::IdString type, const std::set &inputs, const std::set &outputs, bool is_evaluable = false) + void setup_type(RTLIL::IdString type, const pool &inputs, const pool &outputs, bool is_evaluable = false) { CellType ct = {type, inputs, outputs, is_evaluable}; cell_types[ct.type] = ct; @@ -63,7 +63,7 @@ struct CellTypes void setup_module(RTLIL::Module *module) { - std::set inputs, outputs; + pool inputs, outputs; for (RTLIL::IdString wire_name : module->ports) { RTLIL::Wire *wire = module->wire(wire_name); if (wire->port_input) @@ -96,82 +96,105 @@ struct CellTypes "$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"; + for (auto type : unary_ops) - setup_type(type, {"\\A"}, {"\\Y"}, true); + setup_type(type, {A}, {Y}, true); for (auto type : binary_ops) - setup_type(type, {"\\A", "\\B"}, {"\\Y"}, true); + setup_type(type, {A, B}, {Y}, true); for (auto type : std::vector({"$mux", "$pmux"})) - setup_type(type, {"\\A", "\\B", "\\S"}, {"\\Y"}, true); + setup_type(type, {A, B, S}, {Y}, true); - setup_type("$lcu", {"\\P", "\\G", "\\CI"}, {"\\CO"}, true); - setup_type("$alu", {"\\A", "\\B", "\\CI", "\\BI"}, {"\\X", "\\Y", "\\CO"}, true); - setup_type("$fa", {"\\A", "\\B", "\\C"}, {"\\X", "\\Y"}, true); + setup_type("$lcu", {P, G, CI}, {CO}, true); + setup_type("$alu", {A, B, CI, BI}, {X, Y, CO}, true); + setup_type("$fa", {A, B, C}, {X, Y}, true); - setup_type("$assert", {"\\A", "\\EN"}, std::set(), true); + setup_type("$assert", {A, EN}, pool(), true); + setup_type("$assume", {A, EN}, pool(), true); + setup_type("$equiv", {A, B}, {Y}, true); } void setup_internals_mem() { - setup_type("$sr", {"\\SET", "\\CLR"}, {"\\Q"}); - setup_type("$dff", {"\\CLK", "\\D"}, {"\\Q"}); - setup_type("$dffsr", {"\\CLK", "\\SET", "\\CLR", "\\D"}, {"\\Q"}); - setup_type("$adff", {"\\CLK", "\\ARST", "\\D"}, {"\\Q"}); - setup_type("$dlatch", {"\\EN", "\\D"}, {"\\Q"}); - setup_type("$dlatchsr", {"\\EN", "\\SET", "\\CLR", "\\D"}, {"\\Q"}); + IdString SET = "\\SET", CLR = "\\CLR", CLK = "\\CLK", ARST = "\\ARST", EN = "\\EN"; + IdString Q = "\\Q", D = "\\D", ADDR = "\\ADDR", DATA = "\\DATA"; + 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"; - setup_type("$memrd", {"\\CLK", "\\ADDR"}, {"\\DATA"}); - setup_type("$memwr", {"\\CLK", "\\EN", "\\ADDR", "\\DATA"}, std::set()); - setup_type("$mem", {"\\RD_CLK", "\\RD_ADDR", "\\WR_CLK", "\\WR_EN", "\\WR_ADDR", "\\WR_DATA"}, {"\\RD_DATA"}); + setup_type("$sr", {SET, CLR}, {Q}); + setup_type("$dff", {CLK, D}, {Q}); + setup_type("$dffe", {CLK, EN, D}, {Q}); + setup_type("$dffsr", {CLK, SET, CLR, D}, {Q}); + setup_type("$adff", {CLK, ARST, D}, {Q}); + setup_type("$dlatch", {EN, D}, {Q}); + setup_type("$dlatchsr", {EN, SET, CLR, D}, {Q}); - setup_type("$fsm", {"\\CLK", "\\ARST", "\\CTRL_IN"}, {"\\CTRL_OUT"}); + setup_type("$memrd", {CLK, ADDR}, {DATA}); + setup_type("$memwr", {CLK, EN, ADDR, DATA}, pool()); + setup_type("$meminit", {ADDR, DATA}, pool()); + setup_type("$mem", {RD_CLK, RD_ADDR, WR_CLK, WR_EN, WR_ADDR, WR_DATA}, {RD_DATA}); + + setup_type("$fsm", {CLK, ARST, CTRL_IN}, {CTRL_OUT}); } void setup_stdcells() { - setup_type("$_NOT_", {"\\A"}, {"\\Y"}, true); - setup_type("$_AND_", {"\\A", "\\B"}, {"\\Y"}, true); - setup_type("$_NAND_", {"\\A", "\\B"}, {"\\Y"}, true); - setup_type("$_OR_", {"\\A", "\\B"}, {"\\Y"}, true); - setup_type("$_NOR_", {"\\A", "\\B"}, {"\\Y"}, true); - setup_type("$_XOR_", {"\\A", "\\B"}, {"\\Y"}, true); - setup_type("$_XNOR_", {"\\A", "\\B"}, {"\\Y"}, true); - setup_type("$_MUX_", {"\\A", "\\B", "\\S"}, {"\\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); + IdString A = "\\A", B = "\\B", C = "\\C", D = "\\D", S = "\\S", Y = "\\Y"; + setup_type("$_BUF_", {A}, {Y}, true); + setup_type("$_NOT_", {A}, {Y}, true); + setup_type("$_AND_", {A, B}, {Y}, true); + setup_type("$_NAND_", {A, B}, {Y}, true); + setup_type("$_OR_", {A, B}, {Y}, true); + setup_type("$_NOR_", {A, B}, {Y}, true); + setup_type("$_XOR_", {A, B}, {Y}, true); + setup_type("$_XNOR_", {A, B}, {Y}, true); + setup_type("$_MUX_", {A, B, S}, {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); } void setup_stdcells_mem() { + IdString S = "\\S", R = "\\R", C = "\\C"; + IdString D = "\\D", Q = "\\Q", E = "\\E"; + std::vector list_np = {'N', 'P'}, list_01 = {'0', '1'}; for (auto c1 : list_np) for (auto c2 : list_np) - setup_type(stringf("$_SR_%c%c_", c1, c2), {"\\S", "\\R"}, {"\\Q"}); + setup_type(stringf("$_SR_%c%c_", c1, c2), {S, R}, {Q}); for (auto c1 : list_np) - setup_type(stringf("$_DFF_%c_", c1), {"\\C", "\\D"}, {"\\Q"}); + setup_type(stringf("$_DFF_%c_", c1), {C, D}, {Q}); + + for (auto c1 : list_np) + for (auto c2 : list_np) + setup_type(stringf("$_DFFE_%c%c_", c1, c2), {C, D, E}, {Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_01) - setup_type(stringf("$_DFF_%c%c%c_", c1, c2, c3), {"\\C", "\\R", "\\D"}, {"\\Q"}); + setup_type(stringf("$_DFF_%c%c%c_", c1, c2, c3), {C, R, D}, {Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_np) - setup_type(stringf("$_DFFSR_%c%c%c_", c1, c2, c3), {"\\C", "\\S", "\\R", "\\D"}, {"\\Q"}); + setup_type(stringf("$_DFFSR_%c%c%c_", c1, c2, c3), {C, S, R, D}, {Q}); for (auto c1 : list_np) - setup_type(stringf("$_DLATCH_%c_", c1), {"\\E", "\\D"}, {"\\Q"}); + setup_type(stringf("$_DLATCH_%c_", c1), {E, D}, {Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_np) - setup_type(stringf("$_DLATCHSR_%c%c%c_", c1, c2, c3), {"\\E", "\\S", "\\R", "\\D"}, {"\\Q"}); + setup_type(stringf("$_DLATCHSR_%c%c%c_", c1, c2, c3), {E, S, R, D}, {Q}); } void clear() @@ -261,6 +284,8 @@ struct CellTypes HANDLE_CELL_TYPE(neg) #undef HANDLE_CELL_TYPE + if (type == "$_BUF_") + return arg1; if (type == "$_NOT_") return eval_not(arg1); if (type == "$_AND_") @@ -300,7 +325,7 @@ struct CellTypes int width = cell->parameters.at("\\WIDTH").as_int(); std::vector t = cell->parameters.at("\\LUT").bits; - while (SIZE(t) < (1 << width)) + while (GetSize(t) < (1 << width)) t.push_back(RTLIL::S0); t.resize(1 << width); @@ -308,16 +333,16 @@ struct CellTypes RTLIL::State sel = arg1.bits.at(i); std::vector new_t; if (sel == RTLIL::S0) - new_t = std::vector(t.begin(), t.begin() + SIZE(t)/2); + new_t = std::vector(t.begin(), t.begin() + GetSize(t)/2); else if (sel == RTLIL::S1) - new_t = std::vector(t.begin() + SIZE(t)/2, t.end()); + new_t = std::vector(t.begin() + GetSize(t)/2, t.end()); else - for (int j = 0; j < SIZE(t)/2; j++) - new_t.push_back(t[j] == t[j + SIZE(t)/2] ? t[j] : RTLIL::Sx); + for (int j = 0; j < GetSize(t)/2; j++) + new_t.push_back(t[j] == t[j + GetSize(t)/2] ? t[j] : RTLIL::Sx); t.swap(new_t); } - log_assert(SIZE(t) == 1); + log_assert(GetSize(t) == 1); return t; } @@ -360,6 +385,9 @@ struct CellTypes } }; +// initialized by yosys_setup() +extern CellTypes yosys_celltypes; + YOSYS_NAMESPACE_END #endif diff --git a/kernel/consteval.h b/kernel/consteval.h index 2d29d3f7..c2e9710f 100644 --- a/kernel/consteval.h +++ b/kernel/consteval.h @@ -25,6 +25,8 @@ #include "kernel/celltypes.h" #include "kernel/macc.h" +YOSYS_NAMESPACE_BEGIN + struct ConstEval { RTLIL::Module *module; @@ -72,7 +74,7 @@ struct ConstEval assign_map.apply(sig); #ifndef NDEBUG RTLIL::SigSpec current_val = values_map(sig); - for (int i = 0; i < SIZE(current_val); i++) + for (int i = 0; i < GetSize(current_val); i++) log_assert(current_val[i].wire != NULL || current_val[i] == value.bits[i]); #endif values_map.add(sig, RTLIL::SigSpec(value)); @@ -107,10 +109,10 @@ struct ConstEval if (sig_p.is_fully_def() && sig_g.is_fully_def() && sig_ci.is_fully_def()) { - RTLIL::Const coval(RTLIL::Sx, SIZE(sig_co)); + RTLIL::Const coval(RTLIL::Sx, GetSize(sig_co)); bool carry = sig_ci.as_bool(); - for (int i = 0; i < SIZE(coval); i++) { + for (int i = 0; i < GetSize(coval); i++) { carry = (sig_g[i] == RTLIL::S1) || (sig_p[i] == RTLIL::S1 && carry); coval.bits[i] = carry ? RTLIL::S1 : RTLIL::S0; } @@ -118,7 +120,7 @@ struct ConstEval set(sig_co, coval); } else - set(sig_co, RTLIL::Const(RTLIL::Sx, SIZE(sig_co))); + set(sig_co, RTLIL::Const(RTLIL::Sx, GetSize(sig_co))); return true; } @@ -196,7 +198,7 @@ struct ConstEval { RTLIL::SigSpec sig_c = cell->getPort("\\C"); RTLIL::SigSpec sig_x = cell->getPort("\\X"); - int width = SIZE(sig_c); + int width = GetSize(sig_c); if (!eval(sig_a, undef, cell)) return false; @@ -214,7 +216,7 @@ struct ConstEval RTLIL::Const t3 = const_and(sig_c.as_const(), t1, false, false, width); RTLIL::Const val_x = const_or(t2, t3, false, false, width); - for (int i = 0; i < SIZE(val_y); i++) + for (int i = 0; i < GetSize(val_y); i++) if (val_y.bits[i] == RTLIL::Sx) val_x.bits[i] = RTLIL::Sx; @@ -245,13 +247,13 @@ struct ConstEval RTLIL::SigSpec sig_co = cell->getPort("\\CO"); bool any_input_undef = !(sig_a.is_fully_def() && sig_b.is_fully_def() && sig_ci.is_fully_def() && sig_bi.is_fully_def()); - sig_a.extend_u0(SIZE(sig_y), signed_a); - sig_b.extend_u0(SIZE(sig_y), signed_b); + sig_a.extend_u0(GetSize(sig_y), signed_a); + sig_b.extend_u0(GetSize(sig_y), signed_b); bool carry = sig_ci[0] == RTLIL::S1; bool b_inv = sig_bi[0] == RTLIL::S1; - for (int i = 0; i < SIZE(sig_y); i++) + for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::SigSpec x_inputs = { sig_a[i], sig_b[i], sig_bi[0] }; @@ -292,7 +294,7 @@ struct ConstEval return false; } - RTLIL::Const result(0, SIZE(cell->getPort("\\Y"))); + RTLIL::Const result(0, GetSize(cell->getPort("\\Y"))); if (!macc.eval(result)) log_abort(); @@ -376,4 +378,6 @@ struct ConstEval } }; +YOSYS_NAMESPACE_END + #endif diff --git a/kernel/cost.h b/kernel/cost.h new file mode 100644 index 00000000..c6c631e0 --- /dev/null +++ b/kernel/cost.h @@ -0,0 +1,84 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 COST_H +#define COST_H + +#include + +YOSYS_NAMESPACE_BEGIN + +int get_cell_cost(RTLIL::Cell *cell, dict *mod_cost_cache = nullptr); + +int get_cell_cost(RTLIL::IdString type, const dict ¶meters = dict(), + RTLIL::Design *design = nullptr, dict *mod_cost_cache = nullptr) +{ + static dict gate_cost = { + { "$_BUF_", 1 }, + { "$_NOT_", 2 }, + { "$_AND_", 4 }, + { "$_NAND_", 4 }, + { "$_OR_", 4 }, + { "$_NOR_", 4 }, + { "$_XOR_", 8 }, + { "$_XNOR_", 8 }, + { "$_AOI3_", 6 }, + { "$_OAI3_", 6 }, + { "$_AOI4_", 8 }, + { "$_OAI4_", 8 }, + { "$_MUX_", 4 } + }; + + if (gate_cost.count(type)) + return gate_cost.at(type); + + if (parameters.empty() && design && design->module(type)) + { + RTLIL::Module *mod = design->module(type); + + if (mod->attributes.count("\\cost")) + return mod->attributes.at("\\cost").as_int(); + + dict local_mod_cost_cache; + if (mod_cost_cache == nullptr) + mod_cost_cache = &local_mod_cost_cache; + + if (mod_cost_cache->count(mod->name)) + return mod_cost_cache->at(mod->name); + + int module_cost = 1; + for (auto c : mod->cells()) + module_cost += get_cell_cost(c, mod_cost_cache); + + (*mod_cost_cache)[mod->name] = module_cost; + return module_cost; + } + + log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(type), GetSize(parameters)); + return 1; +} + +int get_cell_cost(RTLIL::Cell *cell, dict *mod_cost_cache) +{ + return get_cell_cost(cell->type, cell->parameters, cell->module->design, mod_cost_cache); +} + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/driver.cc b/kernel/driver.cc index f26d9ef8..dda27c6a 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -27,13 +27,98 @@ #include #include -#include -#include #include #include +#ifdef __linux__ +# include +# include +#endif + +#if !defined(_WIN32) || defined(__MINGW32__) +# include +#else +char *optarg; +int optind = 1, optcur = 1; +int getopt(int argc, char **argv, const char *optstring) +{ + if (optind >= argc || argv[optind][0] != '-') + return -1; + + bool takes_arg = false; + int opt = argv[optind][optcur]; + for (int i = 0; optstring[i]; i++) + if (opt == optstring[i] && optstring[i + 1] == ':') + takes_arg = true; + + if (!takes_arg) { + if (argv[optind][++optcur] == 0) + optind++, optcur = 1; + return opt; + } + + if (argv[optind][++optcur]) { + optarg = argv[optind++] + optcur; + optcur = 1; + return opt; + } + + optarg = argv[++optind]; + optind++, optcur = 1; + return opt; +} +#endif + + USING_YOSYS_NAMESPACE +#ifdef EMSCRIPTEN +# include +# include + +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"; @@ -47,6 +132,9 @@ int main(int argc, char **argv) bool print_banner = true; bool print_stats = true; bool call_abort = false; + bool timing_details = false; + bool mode_v = false; + bool mode_q = false; #ifdef YOSYS_ENABLE_READLINE int history_offset = 0; @@ -58,11 +146,103 @@ int main(int argc, char **argv) } #endif + if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help"))) + { + printf("\n"); + printf("Usage: %s [options] [ [..]]\n", argv[0]); + printf("\n"); + printf(" -Q\n"); + printf(" suppress printing of banner (copyright, disclaimer, version)\n"); + printf("\n"); + printf(" -T\n"); + printf(" suppress printing of footer (log hash, version, timing statistics)\n"); + printf("\n"); + printf(" -q\n"); + printf(" quiet operation. only write warnings and error messages to console\n"); + printf(" use this option twice to also quiet warning messages\n"); + printf("\n"); + printf(" -v \n"); + printf(" print log headers up to level to the console. (this\n"); + printf(" implies -q for everything except the 'End of script.' message.)\n"); + printf("\n"); + printf(" -t\n"); + printf(" annotate all log messages with a time stamp\n"); + printf("\n"); + printf(" -d\n"); + printf(" print more detailed timing stats at exit\n"); + printf("\n"); + printf(" -l logfile\n"); + printf(" write log messages to the specified file\n"); + printf("\n"); + printf(" -L logfile\n"); + printf(" like -l but open log file in line buffered mode\n"); + printf("\n"); + printf(" -o outfile\n"); + printf(" write the design to the specified file on exit\n"); + printf("\n"); + 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("\n"); + printf(" -H\n"); + printf(" print the command list\n"); + printf("\n"); + printf(" -h command\n"); + printf(" print the help message for the specified command\n"); + printf("\n"); + printf(" -s scriptfile\n"); + printf(" execute the commands in the script file\n"); + printf("\n"); + printf(" -c tcl_scriptfile\n"); + printf(" execute the commands in the tcl script file (see 'help tcl' for details)\n"); + printf("\n"); + printf(" -p command\n"); + printf(" execute the commands\n"); + printf("\n"); + printf(" -m module_file\n"); + printf(" load the specified module (aka plugin)\n"); + printf("\n"); + printf(" -X\n"); + printf(" enable tracing of core data structure changes. for debugging\n"); + printf("\n"); + printf(" -M\n"); + printf(" will slightly randomize allocated pointer addresses. for debugging\n"); + printf("\n"); + printf(" -A\n"); + printf(" will call abort() at the end of the script. for debugging\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("\n"); + printf(" yosys -o output.blif -S input.v\n"); + printf("\n"); + printf("For more complex synthesis jobs it is recommended to use the read_* and write_*\n"); + printf("commands in a script file instead of specifying input and output files on the\n"); + printf("command line.\n"); + printf("\n"); + printf("When no commands, script files or input files are specified on the command\n"); + printf("line, yosys automatically enters the interactive command mode. Use the 'help'\n"); + printf("command to get information on the individual commands.\n"); + printf("\n"); + exit(0); + } + int opt; - while ((opt = getopt(argc, argv, "AQTVSm:f:Hh:b:o:p:l:qv:ts:c:")) != -1) + while ((opt = getopt(argc, argv, "MXAQTVSm:f:Hh:b:o:p:l:L:qv:tds:c:")) != -1) { switch (opt) { + case 'M': + memhasher_on(); + break; + case 'X': + yosys_xtrace++; + break; case 'A': call_abort = true; break; @@ -101,22 +281,32 @@ int main(int argc, char **argv) got_output_filename = true; break; case 'l': + case 'L': log_files.push_back(fopen(optarg, "wt")); if (log_files.back() == NULL) { fprintf(stderr, "Can't open log file `%s' for writing!\n", optarg); exit(1); } + if (opt == 'L') + setvbuf(log_files.back(), NULL, _IOLBF, 0); break; case 'q': + mode_q = true; + if (log_errfile == stderr) + log_quiet_warnings = true; log_errfile = stderr; break; case 'v': + mode_v = true; log_errfile = stderr; log_verbose_level = atoi(optarg); break; case 't': log_time = true; break; + case 'd': + timing_details = true; + break; case 's': scriptfile = optarg; scriptfile_tcl = false; @@ -126,107 +316,19 @@ int main(int argc, char **argv) scriptfile_tcl = true; break; default: - fprintf(stderr, "\n"); - fprintf(stderr, "Usage: %s [-V -S -Q -T -q] [-v [-t] [-l ] [-o ] [-f ] [-h cmd] \\\n", argv[0]); - fprintf(stderr, " %*s[{-s|-c} ] [-p [-p ..]] [-b ] [-m ] [ [..]]\n", int(strlen(argv[0])+1), ""); - fprintf(stderr, "\n"); - fprintf(stderr, " -Q\n"); - fprintf(stderr, " suppress printing of banner (copyright, disclaimer, version)\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -T\n"); - fprintf(stderr, " suppress printing of footer (log hash, version, timing statistics)\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -q\n"); - fprintf(stderr, " quiet operation. only write error messages to console\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -v \n"); - fprintf(stderr, " print log headers up to level to the console. (implies -q)\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -t\n"); - fprintf(stderr, " annotate all log messages with a time stamp\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -l logfile\n"); - fprintf(stderr, " write log messages to the specified file\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -o outfile\n"); - fprintf(stderr, " write the design to the specified file on exit\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -b backend\n"); - fprintf(stderr, " use this backend for the output file specified on the command line\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -f backend\n"); - fprintf(stderr, " use the specified front for the input files on the command line\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -H\n"); - fprintf(stderr, " print the command list\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -h command\n"); - fprintf(stderr, " print the help message for the specified command\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -s scriptfile\n"); - fprintf(stderr, " execute the commands in the script file\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -c tcl_scriptfile\n"); - fprintf(stderr, " execute the commands in the tcl script file (see 'help tcl' for details)\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -p command\n"); - fprintf(stderr, " execute the commands\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -m module_file\n"); - fprintf(stderr, " load the specified module (aka plugin)\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -A\n"); - fprintf(stderr, " will call abort() at the end of the script. useful for debugging\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -V\n"); - fprintf(stderr, " print version information and exit\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "The option -S is an shortcut for calling the \"synth\" command, a default\n"); - fprintf(stderr, "script for transforming the verilog input to a gate-level netlist. For example:\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " yosys -o output.blif -S input.v\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "For more complex synthesis jobs it is recommended to use the read_* and write_*\n"); - fprintf(stderr, "commands in a script file instead of specifying input and output files on the\n"); - fprintf(stderr, "command line.\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "When no commands, script files or input files are specified on the command\n"); - fprintf(stderr, "line, yosys automatically enters the interactive command mode. Use the 'help'\n"); - fprintf(stderr, "command to get information on the individual commands.\n"); - fprintf(stderr, "\n"); + fprintf(stderr, "Run '%s -h' for help.\n", argv[0]); exit(1); } } - if (log_errfile == NULL) - log_files.push_back(stderr); - - if (print_banner) { - log("\n"); - log(" /-----------------------------------------------------------------------------\\\n"); - log(" | |\n"); - log(" | yosys -- Yosys Open SYnthesis Suite |\n"); - log(" | |\n"); - log(" | Copyright (C) 2012 Clifford Wolf |\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"); - log(" | copyright notice and this permission notice appear in all copies. |\n"); - log(" | |\n"); - log(" | THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |\n"); - log(" | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |\n"); - log(" | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |\n"); - log(" | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |\n"); - log(" | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |\n"); - log(" | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |\n"); - log(" | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |\n"); - log(" | |\n"); - log(" \\-----------------------------------------------------------------------------/\n"); - log("\n"); - log(" %s\n", yosys_version_str); - log("\n"); + if (log_errfile == NULL) { + log_files.push_back(stdout); + log_error_stderr = true; } + if (print_banner) + yosys_banner(); + if (print_stats) log_hasher = new SHA1; @@ -242,7 +344,7 @@ int main(int argc, char **argv) } while (optind < argc) - run_frontend(argv[optind++], frontend_command, yosys_design, output_filename == "-" ? &backend_command : NULL, NULL); + run_frontend(argv[optind++], frontend_command, output_filename == "-" ? &backend_command : NULL); if (!scriptfile.empty()) { if (scriptfile_tcl) { @@ -253,14 +355,14 @@ int main(int argc, char **argv) log_error("Can't exectue TCL script: this version of yosys is not built with TCL support enabled.\n"); #endif } else - run_frontend(scriptfile, "script", yosys_design, output_filename == "-" ? &backend_command : NULL, NULL); + run_frontend(scriptfile, "script", output_filename == "-" ? &backend_command : NULL); } for (auto it = passes_commands.begin(); it != passes_commands.end(); it++) - run_pass(*it, yosys_design); + run_pass(*it); if (!backend_command.empty()) - run_backend(output_filename, backend_command, yosys_design); + run_backend(output_filename, backend_command); if (print_stats) { @@ -268,12 +370,37 @@ int main(int argc, char **argv) delete log_hasher; log_hasher = nullptr; + log_time = false; + yosys_xtrace = 0; + log_spacer(); + + if (mode_v && !mode_q) + log_files.push_back(stderr); + +#ifdef _WIN32 + log("End of script. Logfile hash: %s\n", hash.c_str()); +#else + std::string meminfo; + std::string stats_divider = ", "; +# ifdef __linux__ + std::ifstream statm; + statm.open(stringf("/proc/%lld/statm", (long long)getpid())); + if (statm.is_open()) { + int sz_total, sz_resident; + statm >> sz_total >> sz_resident; + meminfo = stringf(", MEM: %.2f MB total, %.2f MB resident", + sz_total * (getpagesize() / 1024.0 / 1024.0), + sz_resident * (getpagesize() / 1024.0 / 1024.0)); + stats_divider = "\n"; + } +# endif + struct rusage ru_buffer; getrusage(RUSAGE_SELF, &ru_buffer); - log_spacer(); - log("End of script. Logfile hash: %s, CPU: user %.2fs system %.2fs\n", hash.c_str(), - ru_buffer.ru_utime.tv_sec + 1e-6 * ru_buffer.ru_utime.tv_usec, - ru_buffer.ru_stime.tv_sec + 1e-6 * ru_buffer.ru_stime.tv_usec); + log("End of script. Logfile hash: %s%sCPU: user %.2fs system %.2fs%s\n", hash.c_str(), + stats_divider.c_str(), ru_buffer.ru_utime.tv_sec + 1e-6 * ru_buffer.ru_utime.tv_usec, + ru_buffer.ru_stime.tv_sec + 1e-6 * ru_buffer.ru_stime.tv_usec, meminfo.c_str()); +#endif log("%s\n", yosys_version_str); int64_t total_ns = 0; @@ -285,37 +412,49 @@ int main(int argc, char **argv) timedat.insert(make_tuple(it.second->runtime_ns + 1, it.second->call_counter, it.first)); } - int out_count = 0; - log("Time spent:"); - for (auto it = timedat.rbegin(); it != timedat.rend() && out_count < 4; it++, out_count++) { - if (out_count >= 2 && (std::get<0>(*it) < 1000000000 || int(100*std::get<0>(*it) / total_ns) < 20)) { - log(", ..."); - break; + if (timing_details) + { + log("Time spent:\n"); + for (auto it = timedat.rbegin(); it != timedat.rend(); it++) { + log("%5d%% %5d calls %8.3f sec %s\n", int(100*std::get<0>(*it) / total_ns), + std::get<1>(*it), std::get<0>(*it) / 1000000000.0, std::get<2>(*it).c_str()); } - log("%s %d%% %dx %s (%d sec)", out_count ? "," : "", int(100*std::get<0>(*it) / total_ns), - std::get<1>(*it), std::get<2>(*it).c_str(), int(std::get<0>(*it) / 1000000000)); } - log("%s\n", out_count ? "" : " no commands executed"); + else + { + int out_count = 0; + log("Time spent:"); + for (auto it = timedat.rbegin(); it != timedat.rend() && out_count < 4; it++, out_count++) { + if (out_count >= 2 && (std::get<0>(*it) < 1000000000 || int(100*std::get<0>(*it) / total_ns) < 20)) { + log(", ..."); + break; + } + log("%s %d%% %dx %s (%d sec)", out_count ? "," : "", int(100*std::get<0>(*it) / total_ns), + std::get<1>(*it), std::get<2>(*it).c_str(), int(std::get<0>(*it) / 1000000000)); + } + log("%s\n", out_count ? "" : " no commands executed"); + } } -#ifdef COVER_ACTIVE +#if defined(YOSYS_ENABLE_COVER) && defined(__linux__) if (getenv("YOSYS_COVER_DIR") || getenv("YOSYS_COVER_FILE")) { - char filename_buffer[4096]; + string filename; FILE *f; if (getenv("YOSYS_COVER_DIR")) { - snprintf(filename_buffer, 4096, "%s/yosys_cover_%d_XXXXXX.txt", getenv("YOSYS_COVER_DIR"), getpid()); - f = fdopen(mkstemps(filename_buffer, 4), "w"); + filename = stringf("%s/yosys_cover_%d_XXXXXX.txt", getenv("YOSYS_COVER_DIR"), getpid()); + filename = make_temp_file(filename); } else { - snprintf(filename_buffer, 4096, "%s", getenv("YOSYS_COVER_FILE")); - f = fopen(filename_buffer, "a+"); + filename = getenv("YOSYS_COVER_FILE"); } - if (f == NULL) - log_error("Can't create coverage file `%s'.\n", filename_buffer); + f = fopen(filename.c_str(), "a+"); - log("\n", filename_buffer); + if (f == NULL) + log_error("Can't create coverage file `%s'.\n", filename.c_str()); + + log("\n", filename.c_str()); for (auto &it : get_coverage_data()) fprintf(f, "%-60s %10d %s\n", it.second.first.c_str(), it.second.second, it.first.c_str()); @@ -324,6 +463,7 @@ int main(int argc, char **argv) } #endif + memhasher_off(); if (call_abort) abort(); @@ -347,3 +487,5 @@ int main(int argc, char **argv) return 0; } +#endif /* EMSCRIPTEN */ + diff --git a/kernel/hashlib.h b/kernel/hashlib.h new file mode 100644 index 00000000..94b573e4 --- /dev/null +++ b/kernel/hashlib.h @@ -0,0 +1,887 @@ +// 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 +// means. + +// ------------------------------------------------------- +// Written by Clifford Wolf in 2014 +// ------------------------------------------------------- + +#ifndef HASHLIB_H + +#include +#include +#include +#include + +namespace hashlib { + +const int hashtable_size_trigger = 2; +const int hashtable_size_factor = 3; + +// The XOR version of DJB2 +inline unsigned int mkhash(unsigned int a, unsigned int b) { + return ((a << 5) + a) ^ b; +} + +// traditionally 5381 is used as starting value for the djb2 hash +const unsigned int mkhash_init = 5381; + +// The ADD version of DJB2 +// (usunsigned int mkhashe this version for cache locality in b) +inline unsigned int mkhash_add(unsigned int a, unsigned int b) { + return ((a << 5) + a) + b; +} + +inline unsigned int mkhash_xorshift(unsigned int a) { + if (sizeof(a) == 4) { + a ^= a << 13; + a ^= a >> 17; + a ^= a << 5; + } else if (sizeof(a) == 8) { + a ^= a << 13; + a ^= a >> 7; + a ^= a << 17; + } else + throw std::runtime_error("mkhash_xorshift() only implemented for 32 bit and 64 bit ints"); + return a; +} + +template struct hash_ops { + static inline bool cmp(const T &a, const T &b) { + return a == b; + } + static inline unsigned int hash(const T &a) { + return a.hash(); + } +}; + +template<> struct hash_ops { + template + static inline bool cmp(T a, T b) { + return a == b; + } + template + static inline unsigned int hash(T a) { + return a; + } +}; + +template<> struct hash_ops { + static inline bool cmp(const std::string &a, const std::string &b) { + return a == b; + } + static inline unsigned int hash(const std::string &a) { + unsigned int v = 0; + for (auto c : a) + v = mkhash(v, c); + return v; + } +}; + +template struct hash_ops> { + static inline bool cmp(std::pair a, std::pair b) { + return a == b; + } + static inline unsigned int hash(std::pair a) { + hash_ops

p_ops; + hash_ops q_ops; + return mkhash(p_ops.hash(a.first), q_ops.hash(a.second)); + } +}; + +template struct hash_ops> { + static inline bool cmp(std::vector a, std::vector b) { + return a == b; + } + static inline unsigned int hash(std::vector a) { + hash_ops t_ops; + unsigned int h = mkhash_init; + for (auto k : a) + h = mkhash(h, t_ops.hash(k)); + return h; + } +}; + +struct hash_cstr_ops { + static inline bool cmp(const char *a, const char *b) { + for (int i = 0; a[i] || b[i]; i++) + if (a[i] != b[i]) + return false; + return true; + } + static inline unsigned int hash(const char *a) { + unsigned int hash = mkhash_init; + while (*a) + hash = mkhash(hash, *(a++)); + return hash; + } +}; + +struct hash_ptr_ops { + static inline bool cmp(const void *a, const void *b) { + return a == b; + } + static inline unsigned int hash(const void *a) { + return (unsigned long)a; + } +}; + +struct hash_obj_ops { + static inline bool cmp(const void *a, const void *b) { + return a == b; + } + template + static inline unsigned int hash(const T *a) { + return a->hash(); + } +}; + +inline int hashtable_size(int min_size) +{ + static std::vector zero_and_some_primes = { + 0, 23, 29, 37, 47, 59, 79, 101, 127, 163, 211, 269, 337, 431, 541, 677, + 853, 1069, 1361, 1709, 2137, 2677, 3347, 4201, 5261, 6577, 8231, 10289, + 12889, 16127, 20161, 25219, 31531, 39419, 49277, 61603, 77017, 96281, + 120371, 150473, 188107, 235159, 293957, 367453, 459317, 574157, 717697, + 897133, 1121423, 1401791, 1752239, 2190299, 2737937, 3422429, 4278037, + 5347553, 6684443, 8355563, 10444457, 13055587, 16319519, 20399411, + 25499291, 31874149, 39842687, 49803361, 62254207, 77817767, 97272239, + 121590311, 151987889, 189984863, 237481091, 296851369, 371064217 + }; + + for (auto p : zero_and_some_primes) + if (p >= min_size) return p; + + if (sizeof(int) == 4) + throw std::length_error("hash table exceeded maximum size. use a ILP64 abi for larger tables."); + + for (auto p : zero_and_some_primes) + if (100129 * p > min_size) return 100129 * p; + + throw std::length_error("hash table exceeded maximum size."); +} + +template> class dict; +template> class idict; +template> class pool; + +template +class dict +{ + struct entry_t + { + std::pair udata; + int next; + + entry_t() { } + entry_t(const std::pair &udata, int next) : udata(udata), next(next) { } + entry_t(std::pair &&udata, int next) : udata(std::move(udata)), next(next) { } + }; + + std::vector hashtable; + std::vector entries; + OPS ops; + +#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."); + } +#endif + + int do_hash(const K &key) const + { + unsigned int hash = 0; + if (!hashtable.empty()) + hash = ops.hash(key) % (unsigned int)(hashtable.size()); + return hash; + } + + void do_rehash() + { + hashtable.clear(); + hashtable.resize(hashtable_size(entries.size() * 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())); + int hash = do_hash(entries[i].udata.first); + entries[i].next = hashtable[hash]; + hashtable[hash] = i; + } + } + + int do_erase(int index, int hash) + { + do_assert(index < int(entries.size())); + if (hashtable.empty() || index < 0) + return 0; + + int k = hashtable[hash]; + do_assert(0 <= k && k < int(entries.size())); + + if (k == index) { + hashtable[hash] = entries[index].next; + } else { + while (entries[k].next != index) { + k = entries[k].next; + do_assert(0 <= k && k < int(entries.size())); + } + entries[k].next = entries[index].next; + } + + int back_idx = entries.size()-1; + + if (index != back_idx) + { + 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 { + while (entries[k].next != back_idx) { + k = entries[k].next; + do_assert(0 <= k && k < int(entries.size())); + } + entries[k].next = index; + } + + entries[index] = std::move(entries[back_idx]); + } + + entries.pop_back(); + + if (entries.empty()) + hashtable.clear(); + + return 1; + } + + int do_lookup(const K &key, int &hash) const + { + if (hashtable.empty()) + return -1; + + if (entries.size() * hashtable_size_trigger > hashtable.size()) { + ((dict*)this)->do_rehash(); + hash = do_hash(key); + } + + int index = hashtable[hash]; + + while (index >= 0 && !ops.cmp(entries[index].udata.first, key)) { + index = entries[index].next; + do_assert(-1 <= index && index < int(entries.size())); + } + + return index; + } + + int do_insert(const K &key, int &hash) + { + if (hashtable.empty()) { + entries.push_back(entry_t(std::pair(key, T()), -1)); + do_rehash(); + hash = do_hash(key); + } else { + entries.push_back(entry_t(std::pair(key, T()), hashtable[hash])); + hashtable[hash] = entries.size() - 1; + } + return entries.size() - 1; + } + + int do_insert(const std::pair &value, int &hash) + { + if (hashtable.empty()) { + entries.push_back(entry_t(value, -1)); + do_rehash(); + hash = do_hash(value.first); + } else { + entries.push_back(entry_t(value, hashtable[hash])); + hashtable[hash] = entries.size() - 1; + } + return entries.size() - 1; + } + +public: + class const_iterator : public std::iterator> + { + friend class dict; + protected: + const dict *ptr; + int index; + const_iterator(const dict *ptr, int index) : ptr(ptr), index(index) { } + public: + const_iterator() { } + const_iterator operator++() { index--; return *this; } + bool operator<(const const_iterator &other) const { return index > other.index; } + bool operator==(const const_iterator &other) const { return index == other.index; } + bool operator!=(const const_iterator &other) const { return index != other.index; } + const std::pair &operator*() const { return ptr->entries[index].udata; } + const std::pair *operator->() const { return &ptr->entries[index].udata; } + }; + + class iterator : public std::iterator> + { + friend class dict; + protected: + dict *ptr; + int index; + iterator(dict *ptr, int index) : ptr(ptr), index(index) { } + public: + iterator() { } + iterator operator++() { index--; return *this; } + bool operator<(const iterator &other) const { return index > other.index; } + bool operator==(const iterator &other) const { return index == other.index; } + bool operator!=(const iterator &other) const { return index != other.index; } + std::pair &operator*() { return ptr->entries[index].udata; } + std::pair *operator->() { return &ptr->entries[index].udata; } + const std::pair &operator*() const { return ptr->entries[index].udata; } + const std::pair *operator->() const { return &ptr->entries[index].udata; } + operator const_iterator() const { return const_iterator(ptr, index); } + }; + + dict() + { + } + + dict(const dict &other) + { + entries = other.entries; + do_rehash(); + } + + dict(dict &&other) + { + swap(other); + } + + dict &operator=(const dict &other) { + entries = other.entries; + do_rehash(); + return *this; + } + + dict &operator=(dict &&other) { + clear(); + swap(other); + return *this; + } + + dict(const std::initializer_list> &list) + { + for (auto &it : list) + insert(it); + } + + template + dict(InputIterator first, InputIterator last) + { + insert(first, last); + } + + template + void insert(InputIterator first, InputIterator last) + { + for (; first != last; ++first) + insert(*first); + } + + std::pair insert(const K &key) + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + if (i >= 0) + return std::pair(iterator(this, i), false); + i = do_insert(key, hash); + return std::pair(iterator(this, i), true); + } + + std::pair insert(const std::pair &value) + { + int hash = do_hash(value.first); + int i = do_lookup(value.first, hash); + if (i >= 0) + return std::pair(iterator(this, i), false); + i = do_insert(value, hash); + return std::pair(iterator(this, i), true); + } + + int erase(const K &key) + { + int hash = do_hash(key); + int index = do_lookup(key, hash); + return do_erase(index, hash); + } + + iterator erase(iterator it) + { + int hash = do_hash(it->first); + do_erase(it.index, hash); + return ++it; + } + + int count(const K &key) const + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + return i < 0 ? 0 : 1; + } + + int count(const K &key, const_iterator it) const + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + return i < 0 || i > it.index ? 0 : 1; + } + + iterator find(const K &key) + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + if (i < 0) + return end(); + return iterator(this, i); + } + + const_iterator find(const K &key) const + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + if (i < 0) + return end(); + return const_iterator(this, i); + } + + T& at(const K &key) + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + if (i < 0) + throw std::out_of_range("dict::at()"); + return entries[i].udata.second; + } + + const T& at(const K &key) const + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + if (i < 0) + throw std::out_of_range("dict::at()"); + return entries[i].udata.second; + } + + T& operator[](const K &key) + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + if (i < 0) + i = do_insert(std::pair(key, T()), hash); + return entries[i].udata.second; + } + + template> + void sort(Compare comp = Compare()) + { + std::sort(entries.begin(), entries.end(), [comp](const entry_t &a, const entry_t &b){ return comp(b.udata.first, a.udata.first); }); + do_rehash(); + } + + void swap(dict &other) + { + hashtable.swap(other.hashtable); + entries.swap(other.entries); + } + + bool operator==(const dict &other) const { + if (size() != other.size()) + return false; + for (auto &it : entries) { + auto oit = other.find(it.udata.first); + if (oit == other.end() || !(oit->second == it.udata.second)) + return false; + } + return true; + } + + bool operator!=(const dict &other) const { + return !operator==(other); + } + + size_t size() const { return entries.size(); } + bool empty() const { return entries.empty(); } + void clear() { hashtable.clear(); entries.clear(); } + + iterator begin() { return iterator(this, int(entries.size())-1); } + iterator end() { return iterator(nullptr, -1); } + + const_iterator begin() const { return const_iterator(this, int(entries.size())-1); } + const_iterator end() const { return const_iterator(nullptr, -1); } +}; + +template +class pool +{ + template friend class idict; + +protected: + struct entry_t + { + K udata; + int next; + + entry_t() { } + entry_t(const K &udata, int next) : udata(udata), next(next) { } + }; + + std::vector hashtable; + std::vector entries; + OPS ops; + +#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."); + } +#endif + + int do_hash(const K &key) const + { + unsigned int hash = 0; + if (!hashtable.empty()) + hash = ops.hash(key) % (unsigned int)(hashtable.size()); + return hash; + } + + void do_rehash() + { + hashtable.clear(); + hashtable.resize(hashtable_size(entries.size() * 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())); + int hash = do_hash(entries[i].udata); + entries[i].next = hashtable[hash]; + hashtable[hash] = i; + } + } + + int do_erase(int index, int hash) + { + do_assert(index < int(entries.size())); + if (hashtable.empty() || index < 0) + return 0; + + int k = hashtable[hash]; + if (k == index) { + hashtable[hash] = entries[index].next; + } else { + while (entries[k].next != index) { + k = entries[k].next; + do_assert(0 <= k && k < int(entries.size())); + } + entries[k].next = entries[index].next; + } + + int back_idx = entries.size()-1; + + if (index != back_idx) + { + int back_hash = do_hash(entries[back_idx].udata); + + k = hashtable[back_hash]; + if (k == back_idx) { + hashtable[back_hash] = index; + } else { + while (entries[k].next != back_idx) { + k = entries[k].next; + do_assert(0 <= k && k < int(entries.size())); + } + entries[k].next = index; + } + + entries[index] = std::move(entries[back_idx]); + } + + entries.pop_back(); + + if (entries.empty()) + hashtable.clear(); + + return 1; + } + + int do_lookup(const K &key, int &hash) const + { + if (hashtable.empty()) + return -1; + + if (entries.size() * hashtable_size_trigger > hashtable.size()) { + ((pool*)this)->do_rehash(); + hash = do_hash(key); + } + + int index = hashtable[hash]; + + while (index >= 0 && !ops.cmp(entries[index].udata, key)) { + index = entries[index].next; + do_assert(-1 <= index && index < int(entries.size())); + } + + return index; + } + + int do_insert(const K &value, int &hash) + { + if (hashtable.empty()) { + entries.push_back(entry_t(value, -1)); + do_rehash(); + hash = do_hash(value); + } else { + entries.push_back(entry_t(value, hashtable[hash])); + hashtable[hash] = entries.size() - 1; + } + return entries.size() - 1; + } + +public: + class const_iterator : public std::iterator + { + friend class pool; + protected: + const pool *ptr; + int index; + const_iterator(const pool *ptr, int index) : ptr(ptr), index(index) { } + public: + const_iterator() { } + const_iterator operator++() { index--; return *this; } + bool operator==(const const_iterator &other) const { return index == other.index; } + bool operator!=(const const_iterator &other) const { return index != other.index; } + const K &operator*() const { return ptr->entries[index].udata; } + const K *operator->() const { return &ptr->entries[index].udata; } + }; + + class iterator : public std::iterator + { + friend class pool; + protected: + pool *ptr; + int index; + iterator(pool *ptr, int index) : ptr(ptr), index(index) { } + public: + iterator() { } + iterator operator++() { index--; return *this; } + bool operator==(const iterator &other) const { return index == other.index; } + bool operator!=(const iterator &other) const { return index != other.index; } + K &operator*() { return ptr->entries[index].udata; } + K *operator->() { return &ptr->entries[index].udata; } + const K &operator*() const { return ptr->entries[index].udata; } + const K *operator->() const { return &ptr->entries[index].udata; } + operator const_iterator() const { return const_iterator(ptr, index); } + }; + + pool() + { + } + + pool(const pool &other) + { + entries = other.entries; + do_rehash(); + } + + pool(pool &&other) + { + swap(other); + } + + pool &operator=(const pool &other) { + entries = other.entries; + do_rehash(); + return *this; + } + + pool &operator=(pool &&other) { + clear(); + swap(other); + return *this; + } + + pool(const std::initializer_list &list) + { + for (auto &it : list) + insert(it); + } + + template + pool(InputIterator first, InputIterator last) + { + insert(first, last); + } + + template + void insert(InputIterator first, InputIterator last) + { + for (; first != last; ++first) + insert(*first); + } + + std::pair insert(const K &value) + { + int hash = do_hash(value); + int i = do_lookup(value, hash); + if (i >= 0) + return std::pair(iterator(this, i), false); + i = do_insert(value, hash); + return std::pair(iterator(this, i), true); + } + + int erase(const K &key) + { + int hash = do_hash(key); + int index = do_lookup(key, hash); + return do_erase(index, hash); + } + + iterator erase(iterator it) + { + int hash = do_hash(*it); + do_erase(it.index, hash); + return ++it; + } + + int count(const K &key) const + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + return i < 0 ? 0 : 1; + } + + int count(const K &key, const_iterator it) const + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + return i < 0 || i > it.index ? 0 : 1; + } + + iterator find(const K &key) + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + if (i < 0) + return end(); + return iterator(this, i); + } + + const_iterator find(const K &key) const + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + if (i < 0) + return end(); + return const_iterator(this, i); + } + + bool operator[](const K &key) + { + int hash = do_hash(key); + int i = do_lookup(key, hash); + return i >= 0; + } + + template> + void sort(Compare comp = Compare()) + { + std::sort(entries.begin(), entries.end(), [comp](const entry_t &a, const entry_t &b){ return comp(b.udata, a.udata); }); + do_rehash(); + } + + void swap(pool &other) + { + hashtable.swap(other.hashtable); + entries.swap(other.entries); + } + + bool operator==(const pool &other) const { + if (size() != other.size()) + return false; + for (auto &it : entries) + if (!other.count(it.udata)) + return false; + return true; + } + + bool operator!=(const pool &other) const { + return !operator==(other); + } + + size_t size() const { return entries.size(); } + bool empty() const { return entries.empty(); } + void clear() { hashtable.clear(); entries.clear(); } + + iterator begin() { return iterator(this, int(entries.size())-1); } + iterator end() { return iterator(nullptr, -1); } + + const_iterator begin() const { return const_iterator(this, int(entries.size())-1); } + const_iterator end() const { return const_iterator(nullptr, -1); } +}; + +template +class idict +{ + pool database; + +public: + typedef typename pool::const_iterator const_iterator; + + int operator()(const K &key) + { + int hash = database.do_hash(key); + int i = database.do_lookup(key, hash); + if (i < 0) + i = database.do_insert(key, hash); + return i + offset; + } + + int at(const K &key) const + { + int hash = database.do_hash(key); + int i = database.do_lookup(key, hash); + if (i < 0) + throw std::out_of_range("idict::at()"); + return i + offset; + } + + int count(const K &key) const + { + int hash = database.do_hash(key); + int i = database.do_lookup(key, hash); + return i < 0 ? 0 : 1; + } + + void expect(const K &key, int i) + { + int j = (*this)(key); + if (i != j) + throw std::out_of_range("idict::expect()"); + } + + const K &operator[](int index) const + { + return database.entries.at(index - offset).udata; + } + + const_iterator begin() const { return database.begin(); } + const_iterator end() const { return database.end(); } +}; + +} /* namespace hashlib */ + +#endif diff --git a/kernel/log.cc b/kernel/log.cc index 1b0eb664..bf92dace 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -21,7 +21,14 @@ #include "libs/sha1/sha1.h" #include "backends/ilang/ilang_backend.h" -#include +#if !defined(_WIN32) || defined(__MINGW32__) +# include +#endif + +#ifdef __linux__ +# include +#endif + #include #include #include @@ -37,17 +44,41 @@ FILE *log_errfile = NULL; SHA1 *log_hasher = NULL; bool log_time = false; +bool log_error_stderr = false; bool log_cmd_error_throw = false; +bool log_quiet_warnings = false; int log_verbose_level; +string log_last_error; -std::vector header_count; -std::list string_buf; -int string_buf_size = 0; +vector header_count; +pool log_id_cache; +vector string_buf; +int string_buf_index = -1; static struct timeval initial_tv = { 0, 0 }; static bool next_print_log = false; static int log_newline_count = 0; +#if defined(_WIN32) && !defined(__MINGW32__) +// this will get time information and return it in timeval, simulating gettimeofday() +int gettimeofday(struct timeval *tv, struct timezone *tz) +{ + LARGE_INTEGER counter; + LARGE_INTEGER freq; + + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&counter); + + counter.QuadPart *= 1000000; + counter.QuadPart /= freq.QuadPart; + + tv->tv_sec = long(counter.QuadPart / 1000000); + tv->tv_usec = counter.QuadPart % 1000000; + + return 0; +} +#endif + void logv(const char *format, va_list ap) { while (format[0] == '\n' && format[1] != 0) { @@ -62,9 +93,9 @@ void logv(const char *format, va_list ap) size_t nnl_pos = str.find_last_not_of('\n'); if (nnl_pos == std::string::npos) - log_newline_count += SIZE(str); + log_newline_count += GetSize(str); else - log_newline_count = SIZE(str) - nnl_pos - 1; + log_newline_count = GetSize(str) - nnl_pos - 1; if (log_hasher) log_hasher->update(str); @@ -128,15 +159,43 @@ void logv_header(const char *format, va_list ap) log_files.pop_back(); } +void logv_warning(const char *format, va_list ap) +{ + if (log_errfile != NULL && !log_quiet_warnings) + log_files.push_back(log_errfile); + + log("Warning: "); + logv(format, ap); + log_flush(); + + if (log_errfile != NULL && !log_quiet_warnings) + log_files.pop_back(); +} + 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); - log("ERROR: "); - logv(format, ap); + if (log_error_stderr) + for (auto &f : log_files) + if (f == stdout) + f = stderr; + + 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, ...) @@ -155,6 +214,14 @@ void log_header(const char *format, ...) va_end(ap); } +void log_warning(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + logv_warning(format, ap); + va_end(ap); +} + void log_error(const char *format, ...) { va_list ap; @@ -168,10 +235,10 @@ 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_expection(); + throw log_cmd_error_exception(); } logv_error(format, ap); @@ -191,17 +258,112 @@ void log_push() void log_pop() { header_count.pop_back(); + log_id_cache.clear(); string_buf.clear(); - string_buf_size = 0; + string_buf_index = -1; log_flush(); } +#ifdef __linux__ +void log_backtrace(const char *prefix, int levels) +{ + if (levels <= 0) return; + + Dl_info dli; + void *p; + + if ((p = __builtin_extract_return_addr(__builtin_return_address(0))) && dladdr(p, &dli)) { + log("%sframe #1: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr); + } else { + log("%sframe #1: ---\n", prefix); + return; + } + + if (levels <= 1) return; + + if ((p = __builtin_extract_return_addr(__builtin_return_address(1))) && dladdr(p, &dli)) { + log("%sframe #2: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr); + } else { + log("%sframe #2: ---\n", prefix); + return; + } + + if (levels <= 2) return; + + if ((p = __builtin_extract_return_addr(__builtin_return_address(2))) && dladdr(p, &dli)) { + log("%sframe #3: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr); + } else { + log("%sframe #3: ---\n", prefix); + return; + } + + if (levels <= 3) return; + + if ((p = __builtin_extract_return_addr(__builtin_return_address(3))) && dladdr(p, &dli)) { + log("%sframe #4: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr); + } else { + log("%sframe #4: ---\n", prefix); + return; + } + + if (levels <= 4) return; + + if ((p = __builtin_extract_return_addr(__builtin_return_address(4))) && dladdr(p, &dli)) { + log("%sframe #5: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr); + } else { + log("%sframe #5: ---\n", prefix); + return; + } + + if (levels <= 5) return; + + if ((p = __builtin_extract_return_addr(__builtin_return_address(5))) && dladdr(p, &dli)) { + log("%sframe #6: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr); + } else { + log("%sframe #6: ---\n", prefix); + return; + } + + if (levels <= 6) return; + + if ((p = __builtin_extract_return_addr(__builtin_return_address(6))) && dladdr(p, &dli)) { + log("%sframe #7: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr); + } else { + log("%sframe #7: ---\n", prefix); + return; + } + + if (levels <= 7) return; + + if ((p = __builtin_extract_return_addr(__builtin_return_address(7))) && dladdr(p, &dli)) { + log("%sframe #8: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr); + } else { + log("%sframe #8: ---\n", prefix); + return; + } + + if (levels <= 8) return; + + if ((p = __builtin_extract_return_addr(__builtin_return_address(8))) && dladdr(p, &dli)) { + log("%sframe #9: %p %s(%p) %s(%p)\n", prefix, p, dli.dli_fname, dli.dli_fbase, dli.dli_sname, dli.dli_saddr); + } else { + log("%sframe #9: ---\n", prefix); + return; + } + + if (levels <= 9) return; +} +#else +void log_backtrace(const char*, int) { } +#endif + void log_reset_stack() { while (header_count.size() > 1) header_count.pop_back(); + log_id_cache.clear(); string_buf.clear(); - string_buf_size = 0; + string_buf_index = -1; log_flush(); } @@ -223,22 +385,28 @@ const char *log_signal(const RTLIL::SigSpec &sig, bool autoint) std::stringstream buf; ILANG_BACKEND::dump_sigspec(buf, sig, autoint); - if (string_buf_size < 100) - string_buf_size++; - else - string_buf.pop_front(); - string_buf.push_back(buf.str()); - - return string_buf.back().c_str(); + if (string_buf.size() < 100) { + string_buf.push_back(buf.str()); + return string_buf.back().c_str(); + } else { + if (++string_buf_index == 100) + string_buf_index = 0; + string_buf[string_buf_index] = buf.str(); + return string_buf[string_buf_index].c_str(); + } } const char *log_id(RTLIL::IdString str) { + log_id_cache.insert(str); const char *p = str.c_str(); - log_assert(RTLIL::IdString::global_refcount_storage_[str.index_] > 1); - if (p[0] == '\\' && p[1] != '$' && p[1] != 0) - return p+1; - return p; + if (p[0] != '\\') + return p; + if (p[1] == '$' || p[1] == '\\' || p[1] == 0) + return p; + if (p[1] >= '0' && p[1] <= '9') + return p; + return p+1; } void log_cell(RTLIL::Cell *cell, std::string indent) @@ -251,9 +419,9 @@ void log_cell(RTLIL::Cell *cell, std::string indent) // --------------------------------------------------- // This is the magic behind the code coverage counters // --------------------------------------------------- -#ifdef COVER_ACTIVE +#if defined(YOSYS_ENABLE_COVER) && defined(__linux__) -std::map> extra_coverage_data; +dict> extra_coverage_data; void cover_extra(std::string parent, std::string id, bool increment) { if (extra_coverage_data.count(id) == 0) { @@ -266,9 +434,9 @@ void cover_extra(std::string parent, std::string id, bool increment) { extra_coverage_data[id].second++; } -std::map> get_coverage_data() +dict> get_coverage_data() { - std::map> coverage_data; + dict> coverage_data; for (auto &it : pass_register) { std::string key = stringf("passes.%s", it.first.c_str()); @@ -278,14 +446,14 @@ std::map> get_coverage_data() for (auto &it : extra_coverage_data) { if (coverage_data.count(it.first)) - log("WARNING: found duplicate coverage id \"%s\".\n", it.first.c_str()); + log_warning("found duplicate coverage id \"%s\".\n", it.first.c_str()); coverage_data[it.first].first = it.second.first; coverage_data[it.first].second += it.second.second; } for (CoverData *p = __start_yosys_cover_list; p != __stop_yosys_cover_list; p++) { if (coverage_data.count(p->id)) - log("WARNING: found duplicate coverage id \"%s\".\n", p->id); + log_warning("found duplicate coverage id \"%s\".\n", p->id); coverage_data[p->id].first = stringf("%s:%d:%s", p->file, p->line, p->func); coverage_data[p->id].second += p->counter; } diff --git a/kernel/log.h b/kernel/log.h index e2b4db87..16ad7b6c 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -23,8 +23,14 @@ #define LOG_H #include -#include -#include + +#ifndef _WIN32 +# include +# include +#endif + +// from libs/sha1/sha1.h +class SHA1; YOSYS_NAMESPACE_BEGIN @@ -32,30 +38,36 @@ YOSYS_NAMESPACE_BEGIN #define S__LINE__sub1(x) S__LINE__sub2(x) #define S__LINE__ S__LINE__sub1(__LINE__) -struct log_cmd_error_expection { }; +struct log_cmd_error_exception { }; extern std::vector log_files; extern std::vector log_streams; extern FILE *log_errfile; -extern class SHA1 *log_hasher; +extern SHA1 *log_hasher; extern bool log_time; +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_error(const char *format, va_list ap) __attribute__ ((noreturn)); +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, ...) __attribute__ ((format (printf, 1, 2))); -void log_header(const char *format, ...) __attribute__ ((format (printf, 1, 2))); -void log_error(const char *format, ...) __attribute__ ((format (printf, 1, 2))) __attribute__ ((noreturn)); -void log_cmd_error(const char *format, ...) __attribute__ ((format (printf, 1, 2))) __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_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); void log_spacer(); void log_push(); void log_pop(); +void log_backtrace(const char *prefix, int levels); void log_reset_stack(); void log_flush(); @@ -68,36 +80,43 @@ template static inline const char *log_id(T *obj) { void log_cell(RTLIL::Cell *cell, std::string indent = ""); -#define log_abort() log_error("Abort in %s:%d.\n", __FILE__, __LINE__) -#define log_assert(_assert_expr_) do { if (_assert_expr_) break; log_error("Assert `%s' failed in %s:%d.\n", #_assert_expr_, __FILE__, __LINE__); } while (0) -#define log_ping() log("-- %s:%d %s --\n", __FILE__, __LINE__, __PRETTY_FUNCTION__) +#ifndef NDEBUG +static inline void log_assert_worker(bool cond, const char *expr, const char *file, int line) { + if (!cond) log_error("Assert `%s' failed in %s:%d.\n", expr, file, line); +} +# define log_assert(_assert_expr_) YOSYS_NAMESPACE_PREFIX log_assert_worker(_assert_expr_, #_assert_expr_, __FILE__, __LINE__) +#else +# define log_assert(_assert_expr_) +#endif + +#define log_abort() YOSYS_NAMESPACE_PREFIX log_error("Abort in %s:%d.\n", __FILE__, __LINE__) +#define log_ping() YOSYS_NAMESPACE_PREFIX log("-- %s:%d %s --\n", __FILE__, __LINE__, __PRETTY_FUNCTION__) // --------------------------------------------------- // This is the magic behind the code coverage counters // --------------------------------------------------- -#if defined(__linux__) && !defined(NDEBUG) -#define COVER_ACTIVE +#if defined(YOSYS_ENABLE_COVER) && defined(__linux__) #define cover(_id) do { \ - static CoverData __d __attribute__((section("yosys_cover_list"), aligned(1))) = { __FILE__, __FUNCTION__, _id, __LINE__, 0 }; \ + static CoverData __d __attribute__((section("yosys_cover_list"), aligned(1), used)) = { __FILE__, __FUNCTION__, _id, __LINE__, 0 }; \ __d.counter++; \ } while (0) struct CoverData { const char *file, *func, *id; int line, counter; -} __attribute__ ((packed)); +} YS_ATTRIBUTE(packed); // this two symbols are created by the linker for the "yosys_cover_list" ELF section extern "C" struct CoverData __start_yosys_cover_list[]; extern "C" struct CoverData __stop_yosys_cover_list[]; -extern std::map> extra_coverage_data; +extern dict> extra_coverage_data; void cover_extra(std::string parent, std::string id, bool increment = true); -std::map> get_coverage_data(); +dict> get_coverage_data(); #define cover_list(_id, ...) do { cover(_id); \ std::string r = cover_list_worker(_id, __VA_ARGS__); \ @@ -151,6 +170,8 @@ 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 @@ -169,7 +190,7 @@ struct PerformanceTimer } float sec() const { - return total_ns * 1e-9; + return total_ns * 1e-9f; } #else static int64_t query() { return 0; } @@ -188,8 +209,10 @@ static inline void log_dump_val_worker(int v) { log("%d", v); } static inline void log_dump_val_worker(unsigned int v) { log("%u", v); } static inline void log_dump_val_worker(long int v) { log("%ld", v); } static inline void log_dump_val_worker(unsigned long int v) { log("%lu", v); } +#ifndef _WIN32 static inline void log_dump_val_worker(long long int v) { log("%lld", v); } static inline void log_dump_val_worker(unsigned long long int v) { log("%lld", v); } +#endif static inline void log_dump_val_worker(char c) { log(c >= 32 && c < 127 ? "'%c'" : "'\\x%02x'", c); } static inline void log_dump_val_worker(unsigned char c) { log(c >= 32 && c < 127 ? "'%c'" : "'\\x%02x'", c); } static inline void log_dump_val_worker(bool v) { log("%s", v ? "true" : "false"); } @@ -198,7 +221,7 @@ static inline void log_dump_val_worker(char *v) { log("%s", v); } 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) { log_assert(*p == 0); } +static inline void log_dump_args_worker(const char *p YS_ATTRIBUTE(unused)) { log_assert(*p == 0); } void log_dump_val_worker(RTLIL::SigSpec v); template diff --git a/kernel/macc.h b/kernel/macc.h index 27114111..cac5b00d 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -42,17 +42,20 @@ struct Macc for (auto &port : ports) { - if (SIZE(port.in_a) == 0 && SIZE(port.in_b) == 0) + if (GetSize(port.in_a) == 0 && GetSize(port.in_b) == 0) continue; - if (SIZE(port.in_a) == 1 && SIZE(port.in_b) == 0 && !port.is_signed && !port.do_subtract) { + if (GetSize(port.in_a) < GetSize(port.in_b)) + std::swap(port.in_a, port.in_b); + + if (GetSize(port.in_a) == 1 && GetSize(port.in_b) == 0 && !port.is_signed && !port.do_subtract) { bit_ports.append(port.in_a); continue; } if (port.in_a.is_fully_const() && port.in_b.is_fully_const()) { RTLIL::Const v = port.in_a.as_const(); - if (SIZE(port.in_b)) + if (GetSize(port.in_b)) v = const_mul(v, port.in_b.as_const(), port.is_signed, port.is_signed, width); if (port.do_subtract) off = const_sub(off, v, port.is_signed, port.is_signed, width); @@ -62,15 +65,15 @@ struct Macc } if (port.is_signed) { - while (SIZE(port.in_a) > 1 && port.in_a[SIZE(port.in_a)-1] == port.in_a[SIZE(port.in_a)-2]) - port.in_a.remove(SIZE(port.in_a)-1); - while (SIZE(port.in_b) > 1 && port.in_b[SIZE(port.in_b)-1] == port.in_b[SIZE(port.in_b)-2]) - port.in_b.remove(SIZE(port.in_b)-1); + while (GetSize(port.in_a) > 1 && port.in_a[GetSize(port.in_a)-1] == port.in_a[GetSize(port.in_a)-2]) + port.in_a.remove(GetSize(port.in_a)-1); + while (GetSize(port.in_b) > 1 && port.in_b[GetSize(port.in_b)-1] == port.in_b[GetSize(port.in_b)-2]) + port.in_b.remove(GetSize(port.in_b)-1); } else { - while (SIZE(port.in_a) > 1 && port.in_a[SIZE(port.in_a)-1] == RTLIL::S0) - port.in_a.remove(SIZE(port.in_a)-1); - while (SIZE(port.in_b) > 1 && port.in_b[SIZE(port.in_b)-1] == RTLIL::S0) - port.in_b.remove(SIZE(port.in_b)-1); + while (GetSize(port.in_a) > 1 && port.in_a[GetSize(port.in_a)-1] == RTLIL::S0) + port.in_a.remove(GetSize(port.in_a)-1); + while (GetSize(port.in_b) > 1 && port.in_b[GetSize(port.in_b)-1] == RTLIL::S0) + port.in_b.remove(GetSize(port.in_b)-1); } new_ports.push_back(port); @@ -102,10 +105,12 @@ struct Macc bit_ports = cell->getPort("\\B"); std::vector config_bits = cell->getParam("\\CONFIG").bits; - int config_width = cell->getParam("\\CONFIG_WIDTH").as_int(); int config_cursor = 0; - log_assert(SIZE(config_bits) >= config_width); +#ifndef NDEBUG + int config_width = cell->getParam("\\CONFIG_WIDTH").as_int(); + log_assert(GetSize(config_bits) >= config_width); +#endif int num_bits = 0; if (config_bits[config_cursor++] == RTLIL::S1) num_bits |= 1; @@ -114,7 +119,7 @@ struct Macc if (config_bits[config_cursor++] == RTLIL::S1) num_bits |= 8; int port_a_cursor = 0; - while (port_a_cursor < SIZE(port_a)) + while (port_a_cursor < GetSize(port_a)) { log_assert(config_cursor + 2 + 2*num_bits <= config_width); @@ -143,7 +148,7 @@ struct Macc } log_assert(config_cursor == config_width); - log_assert(port_a_cursor == SIZE(port_a)); + log_assert(port_a_cursor == GetSize(port_a)); } void to_cell(RTLIL::Cell *cell) const @@ -153,8 +158,8 @@ struct Macc int max_size = 0, num_bits = 0; for (auto &port : ports) { - max_size = std::max(max_size, SIZE(port.in_a)); - max_size = std::max(max_size, SIZE(port.in_b)); + max_size = std::max(max_size, GetSize(port.in_a)); + max_size = std::max(max_size, GetSize(port.in_b)); } while (max_size) @@ -168,17 +173,17 @@ struct Macc for (auto &port : ports) { - if (SIZE(port.in_a) == 0) + if (GetSize(port.in_a) == 0) continue; config_bits.push_back(port.is_signed ? RTLIL::S1 : RTLIL::S0); config_bits.push_back(port.do_subtract ? RTLIL::S1 : RTLIL::S0); - int size_a = SIZE(port.in_a); + int size_a = GetSize(port.in_a); for (int i = 0; i < num_bits; i++) config_bits.push_back(size_a & (1 << i) ? RTLIL::S1 : RTLIL::S0); - int size_b = SIZE(port.in_b); + int size_b = GetSize(port.in_b); for (int i = 0; i < num_bits; i++) config_bits.push_back(size_b & (1 << i) ? RTLIL::S1 : RTLIL::S0); @@ -189,9 +194,9 @@ struct Macc cell->setPort("\\A", port_a); cell->setPort("\\B", bit_ports); cell->setParam("\\CONFIG", config_bits); - cell->setParam("\\CONFIG_WIDTH", SIZE(config_bits)); - cell->setParam("\\A_WIDTH", SIZE(port_a)); - cell->setParam("\\B_WIDTH", SIZE(bit_ports)); + cell->setParam("\\CONFIG_WIDTH", GetSize(config_bits)); + cell->setParam("\\A_WIDTH", GetSize(port_a)); + cell->setParam("\\B_WIDTH", GetSize(bit_ports)); } bool eval(RTLIL::Const &result) const @@ -205,25 +210,31 @@ struct Macc return false; RTLIL::Const summand; - if (SIZE(port.in_b) == 0) - summand = const_pos(port.in_a.as_const(), port.in_b.as_const(), port.is_signed, port.is_signed, SIZE(result)); + if (GetSize(port.in_b) == 0) + summand = const_pos(port.in_a.as_const(), port.in_b.as_const(), port.is_signed, port.is_signed, GetSize(result)); else - summand = const_mul(port.in_a.as_const(), port.in_b.as_const(), port.is_signed, port.is_signed, SIZE(result)); + summand = const_mul(port.in_a.as_const(), port.in_b.as_const(), port.is_signed, port.is_signed, GetSize(result)); if (port.do_subtract) - result = const_sub(result, summand, port.is_signed, port.is_signed, SIZE(result)); + result = const_sub(result, summand, port.is_signed, port.is_signed, GetSize(result)); else - result = const_add(result, summand, port.is_signed, port.is_signed, SIZE(result)); + result = const_add(result, summand, port.is_signed, port.is_signed, GetSize(result)); } for (auto bit : bit_ports) { if (bit.wire) return false; - result = const_add(result, bit.data, false, false, SIZE(result)); + result = const_add(result, bit.data, false, false, GetSize(result)); } return true; } + + Macc(RTLIL::Cell *cell = nullptr) + { + if (cell != nullptr) + from_cell(cell); + } }; YOSYS_NAMESPACE_END diff --git a/kernel/modtools.h b/kernel/modtools.h index 58cdd5b0..69c13bd3 100644 --- a/kernel/modtools.h +++ b/kernel/modtools.h @@ -33,6 +33,7 @@ struct ModIndex : public RTLIL::Monitor RTLIL::IdString port; int offset; + PortInfo() : cell(), port(), offset() { } PortInfo(RTLIL::Cell* _c, RTLIL::IdString _p, int _o) : cell(_c), port(_p), offset(_o) { } bool operator<(const PortInfo &other) const { @@ -42,24 +43,44 @@ struct ModIndex : public RTLIL::Monitor return offset < other.offset; return port < other.port; } + + bool operator==(const PortInfo &other) const { + return cell == other.cell && port == other.port && offset == other.offset; + } + + unsigned int hash() const { + return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset); + } }; struct SigBitInfo { bool is_input, is_output; - std::set ports; + pool ports; SigBitInfo() : is_input(false), is_output(false) { } + + bool operator==(const SigBitInfo &other) const { + return is_input == other.is_input && is_output == other.is_output && ports == other.ports; + } + + void merge(const SigBitInfo &other) + { + is_input = is_input || other.is_input; + is_output = is_output || other.is_output; + ports.insert(other.ports.begin(), other.ports.end()); + } }; SigMap sigmap; RTLIL::Module *module; std::map database; + int auto_reload_counter; bool auto_reload_module; void port_add(RTLIL::Cell *cell, RTLIL::IdString port, const RTLIL::SigSpec &sig) { - for (int i = 0; i < SIZE(sig); i++) { + for (int i = 0; i < GetSize(sig); i++) { RTLIL::SigBit bit = sigmap(sig[i]); if (bit.wire) database[bit].ports.insert(PortInfo(cell, port, i)); @@ -68,7 +89,7 @@ struct ModIndex : public RTLIL::Monitor void port_del(RTLIL::Cell *cell, RTLIL::IdString port, const RTLIL::SigSpec &sig) { - for (int i = 0; i < SIZE(sig); i++) { + for (int i = 0; i < GetSize(sig); i++) { RTLIL::SigBit bit = sigmap(sig[i]); if (bit.wire) database[bit].ports.erase(PortInfo(cell, port, i)); @@ -80,15 +101,17 @@ struct ModIndex : public RTLIL::Monitor return database[sigmap(bit)]; } - void reload_module() + void reload_module(bool reset_sigmap = true) { - sigmap.clear(); - sigmap.set(module); + if (reset_sigmap) { + sigmap.clear(); + sigmap.set(module); + } database.clear(); for (auto wire : module->wires()) if (wire->port_input || wire->port_output) - for (int i = 0; i < SIZE(wire); i++) { + for (int i = 0; i < GetSize(wire); i++) { RTLIL::SigBit bit = sigmap(RTLIL::SigBit(wire, i)); if (bit.wire && wire->port_input) database[bit].is_input = true; @@ -99,31 +122,105 @@ struct ModIndex : public RTLIL::Monitor for (auto &conn : cell->connections()) port_add(cell, conn.first, conn.second); - auto_reload_module = false; + if (auto_reload_module) { + if (++auto_reload_counter > 2) + log_warning("Auto-reload in ModIndex -- possible performance bug!\n"); + auto_reload_module = false; + } } - virtual void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, RTLIL::SigSpec &sig) OVERRIDE + void check() { +#ifndef NDEBUG if (auto_reload_module) - reload_module(); + return; + + for (auto it : database) + log_assert(it.first == sigmap(it.first)); + + auto database_bak = std::move(database); + reload_module(false); + + if (!(database == database_bak)) + { + for (auto &it : database_bak) + if (!database.count(it.first)) + log("ModuleIndex::check(): Only in database_bak, not database: %s\n", log_signal(it.first)); + + for (auto &it : database) + if (!database_bak.count(it.first)) + log("ModuleIndex::check(): Only in database, not database_bak: %s\n", log_signal(it.first)); + else if (!(it.second == database_bak.at(it.first))) + log("ModuleIndex::check(): Different content for database[%s].\n", log_signal(it.first)); + + log_assert(database == database_bak); + } +#endif + } + + virtual void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, RTLIL::SigSpec &sig) YS_OVERRIDE + { + log_assert(module == cell->module); + + if (auto_reload_module) + return; port_del(cell, port, old_sig); port_add(cell, port, sig); } - virtual void notify_connect(RTLIL::Module *mod, const RTLIL::SigSig&) + virtual void notify_connect(RTLIL::Module *mod YS_ATTRIBUTE(unused), const RTLIL::SigSig &sigsig) YS_OVERRIDE + { + log_assert(module == mod); + + if (auto_reload_module) + return; + + for (int i = 0; i < GetSize(sigsig.first); i++) + { + 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); + + if (!has_lhs && !has_rhs) { + sigmap.add(lhs, rhs); + } else + if (!has_rhs) { + SigBitInfo new_info = database.at(lhs); + database.erase(lhs); + sigmap.add(lhs, rhs); + lhs = sigmap(lhs); + if (lhs.wire) + database[lhs] = new_info; + } else + if (!has_lhs) { + SigBitInfo new_info = database.at(rhs); + database.erase(rhs); + sigmap.add(lhs, rhs); + rhs = sigmap(rhs); + if (rhs.wire) + database[rhs] = new_info; + } else { + SigBitInfo new_info = database.at(lhs); + new_info.merge(database.at(rhs)); + database.erase(lhs); + database.erase(rhs); + sigmap.add(lhs, rhs); + rhs = sigmap(rhs); + if (rhs.wire) + database[rhs] = new_info; + } + } + } + + virtual void notify_connect(RTLIL::Module *mod YS_ATTRIBUTE(unused), const std::vector&) YS_OVERRIDE { log_assert(module == mod); auto_reload_module = true; } - virtual void notify_connect(RTLIL::Module *mod, const std::vector&) - { - log_assert(module == mod); - auto_reload_module = true; - } - - virtual void notify_blackout(RTLIL::Module *mod) + virtual void notify_blackout(RTLIL::Module *mod YS_ATTRIBUTE(unused)) YS_OVERRIDE { log_assert(module == mod); auto_reload_module = true; @@ -131,6 +228,7 @@ struct ModIndex : public RTLIL::Monitor ModIndex(RTLIL::Module *_m) : module(_m) { + auto_reload_counter = 0; auto_reload_module = true; module->monitors.insert(this); } @@ -168,9 +266,9 @@ struct ModIndex : public RTLIL::Monitor return info->is_output; } - std::set &query_ports(RTLIL::SigBit bit) + pool &query_ports(RTLIL::SigBit bit) { - static std::set empty_result_set; + static pool empty_result_set; SigBitInfo *info = query(bit); if (info == nullptr) return empty_result_set; @@ -193,6 +291,14 @@ struct ModWalker return port < other.port; return offset < other.offset; } + + bool operator==(const PortBit &other) const { + return cell == other.cell && port == other.port && offset == other.offset; + } + + unsigned int hash() const { + return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset); + } }; RTLIL::Design *design; @@ -201,11 +307,11 @@ struct ModWalker CellTypes ct; SigMap sigmap; - std::map> signal_drivers; - std::map> signal_consumers; - std::set signal_inputs, signal_outputs; + dict> signal_drivers; + dict> signal_consumers; + pool signal_inputs, signal_outputs; - std::map> cell_outputs, cell_inputs; + dict> cell_outputs, cell_inputs; void add_wire(RTLIL::Wire *wire) { @@ -286,11 +392,11 @@ struct ModWalker // get_* methods -- single RTLIL::SigBit template - inline bool get_drivers(std::set &result, RTLIL::SigBit bit) const + inline bool get_drivers(pool &result, RTLIL::SigBit bit) const { bool found = false; if (signal_drivers.count(bit)) { - const std::set &r = signal_drivers.at(bit); + const pool &r = signal_drivers.at(bit); result.insert(r.begin(), r.end()); found = true; } @@ -298,11 +404,11 @@ struct ModWalker } template - inline bool get_consumers(std::set &result, RTLIL::SigBit bit) const + inline bool get_consumers(pool &result, RTLIL::SigBit bit) const { bool found = false; if (signal_consumers.count(bit)) { - const std::set &r = signal_consumers.at(bit); + const pool &r = signal_consumers.at(bit); result.insert(r.begin(), r.end()); found = true; } @@ -310,7 +416,7 @@ struct ModWalker } template - inline bool get_inputs(std::set &result, RTLIL::SigBit bit) const + inline bool get_inputs(pool &result, RTLIL::SigBit bit) const { bool found = false; if (signal_inputs.count(bit)) @@ -319,7 +425,7 @@ struct ModWalker } template - inline bool get_outputs(std::set &result, RTLIL::SigBit bit) const + inline bool get_outputs(pool &result, RTLIL::SigBit bit) const { bool found = false; if (signal_outputs.count(bit)) @@ -330,12 +436,12 @@ struct ModWalker // get_* methods -- container of RTLIL::SigBit's (always by reference) template - inline bool get_drivers(std::set &result, const T &bits) const + inline bool get_drivers(pool &result, const T &bits) const { bool found = false; for (RTLIL::SigBit bit : bits) if (signal_drivers.count(bit)) { - const std::set &r = signal_drivers.at(bit); + const pool &r = signal_drivers.at(bit); result.insert(r.begin(), r.end()); found = true; } @@ -343,12 +449,12 @@ struct ModWalker } template - inline bool get_consumers(std::set &result, const T &bits) const + inline bool get_consumers(pool &result, const T &bits) const { bool found = false; for (RTLIL::SigBit bit : bits) if (signal_consumers.count(bit)) { - const std::set &r = signal_consumers.at(bit); + const pool &r = signal_consumers.at(bit); result.insert(r.begin(), r.end()); found = true; } @@ -356,7 +462,7 @@ struct ModWalker } template - inline bool get_inputs(std::set &result, const T &bits) const + inline bool get_inputs(pool &result, const T &bits) const { bool found = false; for (RTLIL::SigBit bit : bits) @@ -366,7 +472,7 @@ struct ModWalker } template - inline bool get_outputs(std::set &result, const T &bits) const + inline bool get_outputs(pool &result, const T &bits) const { bool found = false; for (RTLIL::SigBit bit : bits) @@ -377,25 +483,25 @@ struct ModWalker // get_* methods -- call by RTLIL::SigSpec (always by value) - bool get_drivers(std::set &result, RTLIL::SigSpec signal) const + bool get_drivers(pool &result, RTLIL::SigSpec signal) const { std::vector bits = sigmap(signal); return get_drivers(result, bits); } - bool get_consumers(std::set &result, RTLIL::SigSpec signal) const + bool get_consumers(pool &result, RTLIL::SigSpec signal) const { std::vector bits = sigmap(signal); return get_consumers(result, bits); } - bool get_inputs(std::set &result, RTLIL::SigSpec signal) const + bool get_inputs(pool &result, RTLIL::SigSpec signal) const { std::vector bits = sigmap(signal); return get_inputs(result, bits); } - bool get_outputs(std::set &result, RTLIL::SigSpec signal) const + bool get_outputs(pool &result, RTLIL::SigSpec signal) const { std::vector bits = sigmap(signal); return get_outputs(result, bits); @@ -405,47 +511,47 @@ struct ModWalker template inline bool has_drivers(const T &sig) const { - std::set result; + pool result; return get_drivers(result, sig); } template inline bool has_consumers(const T &sig) const { - std::set result; + pool result; return get_consumers(result, sig); } template inline bool has_inputs(const T &sig) const { - std::set result; + pool result; return get_inputs(result, sig); } template inline bool has_outputs(const T &sig) const { - std::set result; + pool result; return get_outputs(result, sig); } // has_* methods -- call by value inline bool has_drivers(RTLIL::SigSpec sig) const { - std::set result; + pool result; return get_drivers(result, sig); } inline bool has_consumers(RTLIL::SigSpec sig) const { - std::set result; + pool result; return get_consumers(result, sig); } inline bool has_inputs(RTLIL::SigSpec sig) const { - std::set result; + pool result; return get_inputs(result, sig); } inline bool has_outputs(RTLIL::SigSpec sig) const { - std::set result; + pool result; return get_outputs(result, sig); } }; diff --git a/kernel/register.cc b/kernel/register.cc index 2f7b89ff..af1cb77b 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -18,6 +18,8 @@ */ #include "kernel/yosys.h" +#include "kernel/satgen.h" + #include #include #include @@ -146,35 +148,39 @@ void Pass::extra_args(std::vector args, size_t argidx, RTLIL::Desig void Pass::call(RTLIL::Design *design, std::string command) { std::vector args; - char *s = strdup(command.c_str()), *sstart = s, *saveptr; - s += strspn(s, " \t\r\n"); - if (*s == 0 || *s == '#') { - free(sstart); + + std::string cmd_buf = command; + std::string tok = next_token(cmd_buf, " \t\r\n"); + + if (tok.empty()) return; - } - if (*s == '!') { - for (s++; *s == ' ' || *s == '\t'; s++) { } - char *p = s + strlen(s) - 1; - while (p >= s && (*p == '\r' || *p == '\n')) - *(p--) = 0; - log_header("Shell command: %s\n", s); - int retCode = system(s); + + if (tok[0] == '!') { + cmd_buf = command.substr(command.find('!') + 1); + 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()); + int retCode = run_command(cmd_buf); if (retCode != 0) log_cmd_error("Shell command returned error code %d.\n", retCode); - free(sstart); return; } - for (char *p = strtok_r(s, " \t\r\n", &saveptr); p; p = strtok_r(NULL, " \t\r\n", &saveptr)) { - std::string str = p; - int strsz = str.size(); - if (str == "#") - break; - if (strsz > 0 && str[strsz-1] == ';') { + + while (!tok.empty()) { + if (tok == "#") { + int stop; + for (stop = 0; stop < GetSize(cmd_buf); stop++) + if (cmd_buf[stop] == '\r' || cmd_buf[stop] == '\n') + break; + cmd_buf = cmd_buf.substr(stop); + } else + if (tok.back() == ';') { int num_semikolon = 0; - while (strsz > 0 && str[strsz-1] == ';') - strsz--, num_semikolon++; - if (strsz > 0) - args.push_back(str.substr(0, strsz)); + while (!tok.empty() && tok.back() == ';') + tok.resize(tok.size()-1), num_semikolon++; + if (!tok.empty()) + args.push_back(tok); call(design, args); args.clear(); if (num_semikolon == 2) @@ -182,9 +188,22 @@ void Pass::call(RTLIL::Design *design, std::string command) if (num_semikolon == 3) call(design, "clean -purge"); } else - args.push_back(str); + args.push_back(tok); + bool found_nl = false; + for (auto c : cmd_buf) { + if (c == ' ' || c == '\t') + continue; + if (c == '\r' || c == '\n') + found_nl = true; + break; + } + if (found_nl) { + call(design, args); + args.clear(); + } + tok = next_token(cmd_buf, " \t\r\n"); } - free(sstart); + call(design, args); } @@ -333,8 +352,8 @@ void Frontend::extra_args(std::istream *&f, std::string &filename, std::vector 0 && (buffer[buffer.size() - 1] == '\n' || buffer[buffer.size() - 1] == '\r')) break; } - int indent = buffer.find_first_not_of(" \t\r\n"); - if (buffer.substr(indent, eot_marker.size()) == eot_marker) + size_t indent = buffer.find_first_not_of(" \t\r\n"); + if (indent != std::string::npos && buffer.substr(indent, eot_marker.size()) == eot_marker) break; last_here_document += buffer; } @@ -528,11 +547,10 @@ struct HelpPass : public Pass { } 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) { @@ -675,6 +693,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 a49675ed..0a10483f 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -71,9 +71,9 @@ 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 args, RTLIL::Design *design) OVERRIDE FINAL; + virtual void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE YS_FINAL; virtual void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) = 0; static std::vector next_args; @@ -87,9 +87,9 @@ 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 args, RTLIL::Design *design) OVERRIDE FINAL; + virtual void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE YS_FINAL; virtual void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) = 0; void extra_args(std::ostream *&f, std::string &filename, std::vector args, size_t argidx); @@ -100,6 +100,8 @@ struct Backend : Pass // implemented in passes/cmds/select.cc extern void handle_extra_select_args(Pass *pass, std::vector args, size_t argidx, size_t args_size, RTLIL::Design *design); +extern RTLIL::Selection eval_select_args(const vector &args, RTLIL::Design *design); +extern void eval_select_op(vector &work, const string &op, RTLIL::Design *design); extern std::map pass_register; extern std::map frontend_register; diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 00be796f..adf89a24 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -19,6 +19,7 @@ #include "kernel/yosys.h" #include "kernel/macc.h" +#include "kernel/celltypes.h" #include "frontends/verilog/verilog_frontend.h" #include "backends/ilang/ilang_backend.h" @@ -27,9 +28,10 @@ YOSYS_NAMESPACE_BEGIN +RTLIL::IdString::destruct_guard_t RTLIL::IdString::destruct_guard; std::vector RTLIL::IdString::global_refcount_storage_; std::vector RTLIL::IdString::global_id_storage_; -std::map RTLIL::IdString::global_id_index_; +dict RTLIL::IdString::global_id_index_; std::vector RTLIL::IdString::global_free_idx_list_; RTLIL::Const::Const() @@ -127,6 +129,21 @@ std::string RTLIL::Const::as_string() const return ret; } +RTLIL::Const RTLIL::Const::from_string(std::string str) +{ + Const c; + for (auto it = str.rbegin(); it != str.rend(); it++) + switch (*it) { + case '0': c.bits.push_back(State::S0); break; + case '1': c.bits.push_back(State::S1); break; + case 'x': c.bits.push_back(State::Sx); break; + case 'z': c.bits.push_back(State::Sz); break; + case 'm': c.bits.push_back(State::Sm); break; + default: c.bits.push_back(State::Sa); + } + return c; +} + std::string RTLIL::Const::decode_string() const { std::string string; @@ -235,13 +252,17 @@ void RTLIL::Selection::optimize(RTLIL::Design *design) RTLIL::Design::Design() { + static unsigned int hashidx_count = 123456789; + hashidx_count = mkhash_xorshift(hashidx_count); + hashidx_ = hashidx_count; + refcount_modules_ = 0; selection_stack.push_back(RTLIL::Selection()); } RTLIL::Design::~Design() { - for (auto it = modules_.begin(); it != modules_.end(); it++) + for (auto it = modules_.begin(); it != modules_.end(); ++it) delete it->second; } @@ -264,6 +285,11 @@ void RTLIL::Design::add(RTLIL::Module *module) for (auto mon : monitors) mon->notify_module_add(module); + + if (yosys_xtrace) { + log("#X# New Module: %s\n", log_id(module)); + log_backtrace("-X- ", yosys_xtrace-1); + } } RTLIL::Module *RTLIL::Design::addModule(RTLIL::IdString name) @@ -279,6 +305,11 @@ RTLIL::Module *RTLIL::Design::addModule(RTLIL::IdString name) for (auto mon : monitors) mon->notify_module_add(module); + if (yosys_xtrace) { + log("#X# New Module: %s\n", log_id(module)); + log_backtrace("-X- ", yosys_xtrace-1); + } + return module; } @@ -348,11 +379,24 @@ void RTLIL::Design::remove(RTLIL::Module *module) for (auto mon : monitors) mon->notify_module_del(module); + if (yosys_xtrace) { + log("#X# Remove Module: %s\n", log_id(module)); + log_backtrace("-X- ", yosys_xtrace-1); + } + log_assert(modules_.at(module->name) == module); modules_.erase(module->name); delete module; } +void RTLIL::Design::sort() +{ + scratchpad.sort(); + modules_.sort(sort_by_id_str()); + for (auto &it : modules_) + it.second->sort(); +} + void RTLIL::Design::check() { #ifndef NDEBUG @@ -417,7 +461,7 @@ std::vector RTLIL::Design::selected_modules() const std::vector result; result.reserve(modules_.size()); for (auto &it : modules_) - if (selected_module(it.first)) + if (selected_module(it.first) && !it.second->get_bool_attribute("\\blackbox")) result.push_back(it.second); return result; } @@ -427,7 +471,7 @@ std::vector RTLIL::Design::selected_whole_modules() const std::vector result; result.reserve(modules_.size()); for (auto &it : modules_) - if (selected_whole_module(it.first)) + if (selected_whole_module(it.first) && !it.second->get_bool_attribute("\\blackbox")) result.push_back(it.second); return result; } @@ -437,15 +481,21 @@ std::vector RTLIL::Design::selected_whole_modules_warn() const std::vector result; result.reserve(modules_.size()); for (auto &it : modules_) - if (selected_whole_module(it.first)) + if (it.second->get_bool_attribute("\\blackbox")) + continue; + else if (selected_whole_module(it.first)) result.push_back(it.second); else if (selected_module(it.first)) - log("Warning: Ignoring partially selected module %s.\n", log_id(it.first)); + log_warning("Ignoring partially selected module %s.\n", log_id(it.first)); return result; } RTLIL::Module::Module() { + static unsigned int hashidx_count = 123456789; + hashidx_count = mkhash_xorshift(hashidx_count); + hashidx_ = hashidx_count; + design = nullptr; refcount_wires_ = 0; refcount_cells_ = 0; @@ -453,17 +503,17 @@ RTLIL::Module::Module() RTLIL::Module::~Module() { - for (auto it = wires_.begin(); it != wires_.end(); it++) + for (auto it = wires_.begin(); it != wires_.end(); ++it) delete it->second; - for (auto it = memories.begin(); it != memories.end(); it++) + for (auto it = memories.begin(); it != memories.end(); ++it) delete it->second; - for (auto it = cells_.begin(); it != cells_.end(); it++) + for (auto it = cells_.begin(); it != cells_.end(); ++it) delete it->second; - for (auto it = processes.begin(); it != processes.end(); it++) + for (auto it = processes.begin(); it != processes.end(); ++it) delete it->second; } -RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, std::map) +RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, dict) { log_error("Module `%s' is used with parameters but is not parametric!\n", id2cstr(name)); } @@ -479,7 +529,7 @@ namespace { { RTLIL::Module *module; RTLIL::Cell *cell; - std::set expected_params, expected_ports; + pool expected_params, expected_ports; InternalCellChecker(RTLIL::Module *module, RTLIL::Cell *cell) : module(module), cell(cell) { } @@ -752,6 +802,17 @@ namespace { return; } + if (cell->type == "$dffe") { + param_bool("\\CLK_POLARITY"); + param_bool("\\EN_POLARITY"); + port("\\CLK", 1); + port("\\EN", 1); + port("\\D", param("\\WIDTH")); + port("\\Q", param("\\WIDTH")); + check_expected(); + return; + } + if (cell->type == "$dffsr") { param_bool("\\CLK_POLARITY"); param_bool("\\SET_POLARITY"); @@ -843,10 +904,20 @@ namespace { return; } + if (cell->type == "$meminit") { + param("\\MEMID"); + param("\\PRIORITY"); + port("\\ADDR", param("\\ABITS")); + port("\\DATA", param("\\WIDTH")); + check_expected(); + return; + } + if (cell->type == "$mem") { param("\\MEMID"); param("\\SIZE"); param("\\OFFSET"); + param("\\INIT"); param_bits("\\RD_CLK_ENABLE", param("\\RD_PORTS")); param_bits("\\RD_CLK_POLARITY", param("\\RD_PORTS")); param_bits("\\RD_TRANSPARENT", param("\\RD_PORTS")); @@ -870,6 +941,22 @@ 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); + port("\\Y", 1); + check_expected(); + return; + } + + if (cell->type == "$_BUF_") { check_gate("AY"); return; } if (cell->type == "$_NOT_") { check_gate("AY"); return; } if (cell->type == "$_AND_") { check_gate("ABY"); return; } if (cell->type == "$_NAND_") { check_gate("ABY"); return; } @@ -891,6 +978,11 @@ namespace { if (cell->type == "$_DFF_N_") { check_gate("DQC"); return; } if (cell->type == "$_DFF_P_") { check_gate("DQC"); return; } + if (cell->type == "$_DFFE_NN_") { check_gate("DQCE"); return; } + if (cell->type == "$_DFFE_NP_") { check_gate("DQCE"); return; } + if (cell->type == "$_DFFE_PN_") { check_gate("DQCE"); return; } + if (cell->type == "$_DFFE_PP_") { check_gate("DQCE"); return; } + if (cell->type == "$_DFF_NN0_") { check_gate("DQCR"); return; } if (cell->type == "$_DFF_NN1_") { check_gate("DQCR"); return; } if (cell->type == "$_DFF_NP0_") { check_gate("DQCR"); return; } @@ -927,6 +1019,21 @@ namespace { } #endif +void RTLIL::Module::sort() +{ + wires_.sort(sort_by_id_str()); + cells_.sort(sort_by_id_str()); + avail_parameters.sort(sort_by_id_str()); + memories.sort(sort_by_id_str()); + processes.sort(sort_by_id_str()); + for (auto &it : cells_) + it.second->sort(); + for (auto &it : wires_) + it.second->attributes.sort(sort_by_id_str()); + for (auto &it : memories) + it.second->attributes.sort(sort_by_id_str()); +} + void RTLIL::Module::check() { #ifndef NDEBUG @@ -940,10 +1047,10 @@ void RTLIL::Module::check() for (auto &it2 : it.second->attributes) log_assert(!it2.first.empty()); if (it.second->port_id) { - log_assert(SIZE(ports) >= it.second->port_id); + log_assert(GetSize(ports) >= it.second->port_id); log_assert(ports.at(it.second->port_id-1) == it.first); log_assert(it.second->port_input || it.second->port_output); - if (SIZE(ports_declared) < it.second->port_id) + if (GetSize(ports_declared) < it.second->port_id) ports_declared.resize(it.second->port_id); log_assert(ports_declared[it.second->port_id-1] == false); ports_declared[it.second->port_id-1] = true; @@ -952,7 +1059,7 @@ void RTLIL::Module::check() } for (auto port_declared : ports_declared) log_assert(port_declared == true); - log_assert(SIZE(ports) == SIZE(ports_declared)); + log_assert(GetSize(ports) == GetSize(ports_declared)); for (auto &it : memories) { log_assert(it.first == it.second->name); @@ -988,6 +1095,7 @@ void RTLIL::Module::check() for (auto &it : connections_) { log_assert(it.first.size() == it.second.size()); + log_assert(!it.first.has_const()); it.first.check(); it.second.check(); } @@ -1006,8 +1114,11 @@ 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->connections_ = connections_; - new_mod->attributes = attributes; + for (auto &conn : connections_) + new_mod->connect(conn); + + for (auto &attr : attributes) + new_mod->attributes[attr.first] = attr.second; for (auto &it : wires_) new_mod->addWire(it.first, it.second); @@ -1061,14 +1172,14 @@ bool RTLIL::Module::has_processes() const bool RTLIL::Module::has_memories_warn() const { if (!memories.empty()) - log("Warning: Ignoring module %s because it contains memories (run 'memory' command first).\n", log_id(this)); + log_warning("Ignoring module %s because it contains memories (run 'memory' command first).\n", log_id(this)); return !memories.empty(); } bool RTLIL::Module::has_processes_warn() const { if (!processes.empty()) - log("Warning: Ignoring module %s because it contains processes (run 'proc' command first).\n", log_id(this)); + log_warning("Ignoring module %s because it contains processes (run 'proc' command first).\n", log_id(this)); return !processes.empty(); } @@ -1114,7 +1225,7 @@ namespace { struct DeleteWireWorker { RTLIL::Module *module; - const std::set *wires_p; + const pool *wires_p; void operator()(RTLIL::SigSpec &sig) { std::vector chunks = sig; @@ -1128,16 +1239,7 @@ namespace { }; } -#if 0 -void RTLIL::Module::remove(RTLIL::Wire *wire) -{ - std::setPort wires_; - wires_.insert(wire); - remove(wires_); -} -#endif - -void RTLIL::Module::remove(const std::set &wires) +void RTLIL::Module::remove(const pool &wires) { log_assert(refcount_wires_ == 0); @@ -1266,6 +1368,11 @@ void RTLIL::Module::connect(const RTLIL::SigSig &conn) for (auto mon : design->monitors) mon->notify_connect(this, conn); + 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); + } + connections_.push_back(conn); } @@ -1283,6 +1390,13 @@ void RTLIL::Module::new_connections(const std::vector &new_conn) for (auto mon : design->monitors) mon->notify_connect(this, new_conn); + if (yosys_xtrace) { + log("#X# New connections vector in %s:\n", log_id(this)); + for (auto &conn: new_conn) + log("#X# %s = %s (%d bits)\n", log_signal(conn.first), log_signal(conn.second), GetSize(conn.first)); + log_backtrace("-X- ", yosys_xtrace-1); + } + connections_ = new_conn; } @@ -1566,6 +1680,15 @@ RTLIL::Cell* RTLIL::Module::addAssert(RTLIL::IdString name, RTLIL::SigSpec sig_a return cell; } +RTLIL::Cell* RTLIL::Module::addEquiv(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y) +{ + RTLIL::Cell *cell = addCell(name, "$equiv"); + cell->setPort("\\A", sig_a); + cell->setPort("\\B", sig_b); + cell->setPort("\\Y", sig_y); + return cell; +} + RTLIL::Cell* RTLIL::Module::addSr(RTLIL::IdString name, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, RTLIL::SigSpec sig_q, bool set_polarity, bool clr_polarity) { RTLIL::Cell *cell = addCell(name, "$sr"); @@ -1578,7 +1701,7 @@ RTLIL::Cell* RTLIL::Module::addSr(RTLIL::IdString name, RTLIL::SigSpec sig_set, return cell; } -RTLIL::Cell* RTLIL::Module::addDff(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity) +RTLIL::Cell* RTLIL::Module::addDff(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity) { RTLIL::Cell *cell = addCell(name, "$dff"); cell->parameters["\\CLK_POLARITY"] = clk_polarity; @@ -1589,6 +1712,19 @@ RTLIL::Cell* RTLIL::Module::addDff(RTLIL::IdString name, RTLIL::SigSpec sig_clk, return cell; } +RTLIL::Cell* RTLIL::Module::addDffe(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity, bool en_polarity) +{ + RTLIL::Cell *cell = addCell(name, "$dffe"); + cell->parameters["\\CLK_POLARITY"] = clk_polarity; + cell->parameters["\\EN_POLARITY"] = en_polarity; + cell->parameters["\\WIDTH"] = sig_q.size(); + cell->setPort("\\CLK", sig_clk); + cell->setPort("\\EN", sig_en); + cell->setPort("\\D", sig_d); + cell->setPort("\\Q", sig_q); + return cell; +} + RTLIL::Cell* RTLIL::Module::addDffsr(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity, bool set_polarity, bool clr_polarity) { @@ -1656,6 +1792,16 @@ RTLIL::Cell* RTLIL::Module::addDffGate(RTLIL::IdString name, RTLIL::SigSpec sig_ return cell; } +RTLIL::Cell* RTLIL::Module::addDffeGate(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity, bool en_polarity) +{ + RTLIL::Cell *cell = addCell(name, stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N')); + cell->setPort("\\C", sig_clk); + cell->setPort("\\E", sig_en); + cell->setPort("\\D", sig_d); + cell->setPort("\\Q", sig_q); + return cell; +} + RTLIL::Cell* RTLIL::Module::addDffsrGate(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity, bool set_polarity, bool clr_polarity) { @@ -1703,6 +1849,10 @@ RTLIL::Cell* RTLIL::Module::addDlatchsrGate(RTLIL::IdString name, RTLIL::SigSpec RTLIL::Wire::Wire() { + static unsigned int hashidx_count = 123456789; + hashidx_count = mkhash_xorshift(hashidx_count); + hashidx_ = hashidx_count; + module = nullptr; width = 1; start_offset = 0; @@ -1714,12 +1864,22 @@ RTLIL::Wire::Wire() RTLIL::Memory::Memory() { + static unsigned int hashidx_count = 123456789; + hashidx_count = mkhash_xorshift(hashidx_count); + hashidx_ = hashidx_count; + width = 1; size = 0; } RTLIL::Cell::Cell() : module(nullptr) { + static unsigned int hashidx_count = 123456789; + hashidx_count = mkhash_xorshift(hashidx_count); + hashidx_ = hashidx_count; + + // log("#memtrace# %p\n", this); + memhasher(); } bool RTLIL::Cell::hasPort(RTLIL::IdString portname) const @@ -1741,6 +1901,11 @@ void RTLIL::Cell::unsetPort(RTLIL::IdString portname) for (auto mon : module->design->monitors) mon->notify_connect(this, conn_it->first, conn_it->second, signal); + if (yosys_xtrace) { + log("#X# Unconnect %s.%s.%s\n", log_id(this->module), log_id(this), log_id(portname)); + log_backtrace("-X- ", yosys_xtrace-1); + } + connections_.erase(conn_it); } } @@ -1753,7 +1918,9 @@ void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal) connections_[portname] = RTLIL::SigSpec(); conn_it = connections_.find(portname); log_assert(conn_it != connections_.end()); - } + } else + if (conn_it->second == signal) + return; for (auto mon : module->monitors) mon->notify_connect(this, conn_it->first, conn_it->second, signal); @@ -1762,6 +1929,11 @@ void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal) for (auto mon : module->design->monitors) mon->notify_connect(this, conn_it->first, conn_it->second, signal); + if (yosys_xtrace) { + log("#X# Connect %s.%s.%s = %s (%d)\n", log_id(this->module), log_id(this), log_id(portname), log_signal(signal), GetSize(signal)); + log_backtrace("-X- ", yosys_xtrace-1); + } + conn_it->second = signal; } @@ -1770,14 +1942,47 @@ const RTLIL::SigSpec &RTLIL::Cell::getPort(RTLIL::IdString portname) const return connections_.at(portname); } -const std::map &RTLIL::Cell::connections() const +const dict &RTLIL::Cell::connections() const { return connections_; } +bool RTLIL::Cell::known() const +{ + if (yosys_celltypes.cell_known(type)) + return true; + if (module && module->design && module->design->module(type)) + return true; + return false; +} + +bool RTLIL::Cell::input(RTLIL::IdString portname) const +{ + if (yosys_celltypes.cell_known(type)) + return yosys_celltypes.cell_input(type, portname); + if (module && module->design) { + RTLIL::Module *m = module->design->module(type); + RTLIL::Wire *w = m ? m->wire(portname) : nullptr; + return w && w->port_input; + } + return false; +} + +bool RTLIL::Cell::output(RTLIL::IdString portname) const +{ + if (yosys_celltypes.cell_known(type)) + return yosys_celltypes.cell_output(type, portname); + if (module && module->design) { + RTLIL::Module *m = module->design->module(type); + RTLIL::Wire *w = m ? m->wire(portname) : nullptr; + return w && w->port_output; + } + return false; +} + bool RTLIL::Cell::hasParam(RTLIL::IdString paramname) const { - return parameters.count(paramname); + return parameters.count(paramname) != 0; } void RTLIL::Cell::unsetParam(RTLIL::IdString paramname) @@ -1795,6 +2000,13 @@ const RTLIL::Const &RTLIL::Cell::getParam(RTLIL::IdString paramname) const return parameters.at(paramname); } +void RTLIL::Cell::sort() +{ + connections_.sort(sort_by_id_str()); + parameters.sort(sort_by_id_str()); + attributes.sort(sort_by_id_str()); +} + void RTLIL::Cell::check() { #ifndef NDEBUG @@ -1810,25 +2022,25 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) return; if (type == "$mux" || type == "$pmux") { - parameters["\\WIDTH"] = SIZE(connections_["\\Y"]); + parameters["\\WIDTH"] = GetSize(connections_["\\Y"]); if (type == "$pmux") - parameters["\\S_WIDTH"] = SIZE(connections_["\\S"]); + parameters["\\S_WIDTH"] = GetSize(connections_["\\S"]); check(); return; } if (type == "$lut") { - parameters["\\WIDTH"] = SIZE(connections_["\\A"]); + parameters["\\WIDTH"] = GetSize(connections_["\\A"]); return; } if (type == "$fa") { - parameters["\\WIDTH"] = SIZE(connections_["\\Y"]); + parameters["\\WIDTH"] = GetSize(connections_["\\Y"]); return; } if (type == "$lcu") { - parameters["\\WIDTH"] = SIZE(connections_["\\CO"]); + parameters["\\WIDTH"] = GetSize(connections_["\\CO"]); return; } @@ -1841,7 +2053,7 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) else if (parameters.count("\\A_SIGNED") == 0) parameters["\\A_SIGNED"] = false; } - parameters["\\A_WIDTH"] = SIZE(connections_["\\A"]); + parameters["\\A_WIDTH"] = GetSize(connections_["\\A"]); } if (connections_.count("\\B")) { @@ -1851,11 +2063,11 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) else if (parameters.count("\\B_SIGNED") == 0) parameters["\\B_SIGNED"] = false; } - parameters["\\B_WIDTH"] = SIZE(connections_["\\B"]); + parameters["\\B_WIDTH"] = GetSize(connections_["\\B"]); } if (connections_.count("\\Y")) - parameters["\\Y_WIDTH"] = SIZE(connections_["\\Y"]); + parameters["\\Y_WIDTH"] = GetSize(connections_["\\Y"]); check(); } @@ -1871,7 +2083,7 @@ RTLIL::SigChunk::SigChunk(const RTLIL::Const &value) { wire = NULL; data = value.bits; - width = SIZE(data); + width = GetSize(data); offset = 0; } @@ -1895,7 +2107,7 @@ RTLIL::SigChunk::SigChunk(const std::string &str) { wire = NULL; data = RTLIL::Const(str).bits; - width = SIZE(data); + width = GetSize(data); offset = 0; } @@ -1903,7 +2115,7 @@ RTLIL::SigChunk::SigChunk(int val, int width) { wire = NULL; data = RTLIL::Const(val, width).bits; - this->width = SIZE(data); + this->width = GetSize(data); offset = 0; } @@ -1911,7 +2123,7 @@ RTLIL::SigChunk::SigChunk(RTLIL::State bit, int width) { wire = NULL; data = RTLIL::Const(bit, width).bits; - this->width = SIZE(data); + this->width = GetSize(data); offset = 0; } @@ -2137,6 +2349,17 @@ RTLIL::SigSpec::SigSpec(std::vector bits) check(); } +RTLIL::SigSpec::SigSpec(pool bits) +{ + cover("kernel.rtlil.sigspec.init.pool_bits"); + + width_ = 0; + hash_ = 0; + for (auto &bit : bits) + append_bit(bit); + check(); +} + RTLIL::SigSpec::SigSpec(std::set bits) { cover("kernel.rtlil.sigspec.init.stdset_bits"); @@ -2148,6 +2371,16 @@ RTLIL::SigSpec::SigSpec(std::set bits) check(); } +RTLIL::SigSpec::SigSpec(bool bit) +{ + cover("kernel.rtlil.sigspec.init.bool"); + + width_ = 0; + hash_ = 0; + append_bit(bit); + check(); +} + void RTLIL::SigSpec::pack() const { RTLIL::SigSpec *that = (RTLIL::SigSpec*)this; @@ -2203,9 +2436,7 @@ void RTLIL::SigSpec::unpack() const that->hash_ = 0; } -#define DJB2(_hash, _value) do { (_hash) = (((_hash) << 5) + (_hash)) + (_value); } while (0) - -void RTLIL::SigSpec::hash() const +void RTLIL::SigSpec::updhash() const { RTLIL::SigSpec *that = (RTLIL::SigSpec*)this; @@ -2215,15 +2446,15 @@ void RTLIL::SigSpec::hash() const cover("kernel.rtlil.sigspec.hash"); that->pack(); - that->hash_ = 5381; + that->hash_ = mkhash_init; for (auto &c : that->chunks_) if (c.wire == NULL) { for (auto &v : c.data) - DJB2(that->hash_, v); + that->hash_ = mkhash(that->hash_, v); } else { - DJB2(that->hash_, c.wire->name.index_); - DJB2(that->hash_, c.offset); - DJB2(that->hash_, c.width); + that->hash_ = mkhash(that->hash_, c.wire->name.index_); + that->hash_ = mkhash(that->hash_, c.offset); + that->hash_ = mkhash(that->hash_, c.width); } if (that->hash_ == 0) @@ -2255,15 +2486,39 @@ void RTLIL::SigSpec::replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec pattern.unpack(); with.unpack(); - std::map rules; + dict rules; - for (int i = 0; i < SIZE(pattern.bits_); i++) + for (int i = 0; i < GetSize(pattern.bits_); i++) if (pattern.bits_[i].wire != NULL) rules[pattern.bits_[i]] = with.bits_[i]; replace(rules, other); } +void RTLIL::SigSpec::replace(const dict &rules) +{ + replace(rules, this); +} + +void RTLIL::SigSpec::replace(const dict &rules, RTLIL::SigSpec *other) const +{ + cover("kernel.rtlil.sigspec.replace_dict"); + + log_assert(other != NULL); + log_assert(width_ == other->width_); + + unpack(); + other->unpack(); + + for (int i = 0; i < GetSize(bits_); i++) { + auto it = rules.find(bits_[i]); + if (it != rules.end()) + other->bits_[i] = it->second; + } + + other->check(); +} + void RTLIL::SigSpec::replace(const std::map &rules) { replace(rules, this); @@ -2271,7 +2526,7 @@ void RTLIL::SigSpec::replace(const std::map &rules void RTLIL::SigSpec::replace(const std::map &rules, RTLIL::SigSpec *other) const { - cover("kernel.rtlil.sigspec.replace"); + cover("kernel.rtlil.sigspec.replace_map"); log_assert(other != NULL); log_assert(width_ == other->width_); @@ -2279,7 +2534,7 @@ void RTLIL::SigSpec::replace(const std::map &rules unpack(); other->unpack(); - for (int i = 0; i < SIZE(bits_); i++) { + for (int i = 0; i < GetSize(bits_); i++) { auto it = rules.find(bits_[i]); if (it != rules.end()) other->bits_[i] = it->second; @@ -2301,22 +2556,22 @@ void RTLIL::SigSpec::remove(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other void RTLIL::SigSpec::remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other) { - std::set pattern_bits = pattern.to_sigbit_set(); + pool pattern_bits = pattern.to_sigbit_pool(); remove2(pattern_bits, other); } -void RTLIL::SigSpec::remove(const std::set &pattern) +void RTLIL::SigSpec::remove(const pool &pattern) { remove2(pattern, NULL); } -void RTLIL::SigSpec::remove(const std::set &pattern, RTLIL::SigSpec *other) const +void RTLIL::SigSpec::remove(const pool &pattern, RTLIL::SigSpec *other) const { RTLIL::SigSpec tmp = *this; tmp.remove2(pattern, other); } -void RTLIL::SigSpec::remove2(const std::set &pattern, RTLIL::SigSpec *other) +void RTLIL::SigSpec::remove2(const pool &pattern, RTLIL::SigSpec *other) { if (other) cover("kernel.rtlil.sigspec.remove_other"); @@ -2332,12 +2587,12 @@ void RTLIL::SigSpec::remove2(const std::set &pattern, RTLIL::SigS std::vector new_bits, new_other_bits; - new_bits.resize(SIZE(bits_)); + new_bits.resize(GetSize(bits_)); if (other != NULL) - new_other_bits.resize(SIZE(bits_)); + new_other_bits.resize(GetSize(bits_)); int k = 0; - for (int i = 0; i < SIZE(bits_); i++) { + for (int i = 0; i < GetSize(bits_); i++) { if (bits_[i].wire != NULL && pattern.count(bits_[i])) continue; if (other != NULL) @@ -2350,11 +2605,11 @@ void RTLIL::SigSpec::remove2(const std::set &pattern, RTLIL::SigS new_other_bits.resize(k); bits_.swap(new_bits); - width_ = SIZE(bits_); + width_ = GetSize(bits_); if (other != NULL) { other->bits_.swap(new_other_bits); - other->width_ = SIZE(other->bits_); + other->width_ = GetSize(other->bits_); } check(); @@ -2362,11 +2617,11 @@ void RTLIL::SigSpec::remove2(const std::set &pattern, RTLIL::SigS RTLIL::SigSpec RTLIL::SigSpec::extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other) const { - std::set pattern_bits = pattern.to_sigbit_set(); + pool pattern_bits = pattern.to_sigbit_pool(); return extract(pattern_bits, other); } -RTLIL::SigSpec RTLIL::SigSpec::extract(const std::set &pattern, const RTLIL::SigSpec *other) const +RTLIL::SigSpec RTLIL::SigSpec::extract(const pool &pattern, const RTLIL::SigSpec *other) const { if (other) cover("kernel.rtlil.sigspec.extract_other"); @@ -2417,7 +2672,7 @@ void RTLIL::SigSpec::remove_const() cover("kernel.rtlil.sigspec.remove_const.packed"); std::vector new_chunks; - new_chunks.reserve(SIZE(chunks_)); + new_chunks.reserve(GetSize(chunks_)); width_ = 0; for (auto &chunk : chunks_) @@ -2539,25 +2794,6 @@ void RTLIL::SigSpec::append_bit(const RTLIL::SigBit &bit) check(); } -void RTLIL::SigSpec::extend(int width, bool is_signed) -{ - cover("kernel.rtlil.sigspec.extend"); - - pack(); - - if (width_ > width) - remove(width, width_ - width); - - if (width_ < width) { - RTLIL::SigSpec padding = width_ > 0 ? extract(width_ - 1, 1) : RTLIL::SigSpec(RTLIL::State::S0); - if (!is_signed && padding != RTLIL::SigSpec(RTLIL::State::Sx) && padding != RTLIL::SigSpec(RTLIL::State::Sz) && - padding != RTLIL::SigSpec(RTLIL::State::Sa) && padding != RTLIL::SigSpec(RTLIL::State::Sm)) - padding = RTLIL::SigSpec(RTLIL::State::S0); - while (width_ < width) - append(padding); - } -} - void RTLIL::SigSpec::extend_u0(int width, bool is_signed) { cover("kernel.rtlil.sigspec.extend_u0"); @@ -2568,9 +2804,9 @@ void RTLIL::SigSpec::extend_u0(int width, bool is_signed) remove(width, width_ - width); if (width_ < width) { - RTLIL::SigSpec padding = width_ > 0 ? extract(width_ - 1, 1) : RTLIL::SigSpec(RTLIL::State::S0); + RTLIL::SigBit padding = width_ > 0 ? (*this)[width_ - 1] : RTLIL::State::S0; if (!is_signed) - padding = RTLIL::SigSpec(RTLIL::State::S0); + padding = RTLIL::State::S0; while (width_ < width) append(padding); } @@ -2623,7 +2859,7 @@ void RTLIL::SigSpec::check() const { cover("kernel.rtlil.sigspec.check.unpacked"); - log_assert(width_ == SIZE(bits_)); + log_assert(width_ == GetSize(bits_)); log_assert(chunks_.empty()); } } @@ -2645,8 +2881,8 @@ bool RTLIL::SigSpec::operator <(const RTLIL::SigSpec &other) const if (chunks_.size() != other.chunks_.size()) return chunks_.size() < other.chunks_.size(); - hash(); - other.hash(); + updhash(); + other.updhash(); if (hash_ != other.hash_) return hash_ < other.hash_; @@ -2677,8 +2913,8 @@ bool RTLIL::SigSpec::operator ==(const RTLIL::SigSpec &other) const if (chunks_.size() != chunks_.size()) return false; - hash(); - other.hash(); + updhash(); + other.updhash(); if (hash_ != other.hash_) return false; @@ -2698,7 +2934,7 @@ bool RTLIL::SigSpec::is_wire() const cover("kernel.rtlil.sigspec.is_wire"); pack(); - return SIZE(chunks_) == 1 && chunks_[0].wire && chunks_[0].wire->width == width_; + return GetSize(chunks_) == 1 && chunks_[0].wire && chunks_[0].wire->width == width_; } bool RTLIL::SigSpec::is_chunk() const @@ -2706,7 +2942,7 @@ bool RTLIL::SigSpec::is_chunk() const cover("kernel.rtlil.sigspec.is_chunk"); pack(); - return SIZE(chunks_) == 1; + return GetSize(chunks_) == 1; } bool RTLIL::SigSpec::is_fully_const() const @@ -2750,6 +2986,17 @@ bool RTLIL::SigSpec::is_fully_undef() const return true; } +bool RTLIL::SigSpec::has_const() const +{ + cover("kernel.rtlil.sigspec.has_const"); + + pack(); + for (auto it = chunks_.begin(); it != chunks_.end(); it++) + if (it->width > 0 && it->wire == NULL) + return true; + return false; +} + bool RTLIL::SigSpec::has_marked_bits() const { cover("kernel.rtlil.sigspec.has_marked_bits"); @@ -2769,7 +3016,7 @@ bool RTLIL::SigSpec::as_bool() const cover("kernel.rtlil.sigspec.as_bool"); pack(); - log_assert(is_fully_const() && SIZE(chunks_) <= 1); + log_assert(is_fully_const() && GetSize(chunks_) <= 1); if (width_) return RTLIL::Const(chunks_[0].data).as_bool(); return false; @@ -2780,7 +3027,7 @@ int RTLIL::SigSpec::as_int(bool is_signed) const cover("kernel.rtlil.sigspec.as_int"); pack(); - log_assert(is_fully_const() && SIZE(chunks_) <= 1); + log_assert(is_fully_const() && GetSize(chunks_) <= 1); if (width_) return RTLIL::Const(chunks_[0].data).as_int(is_signed); return 0; @@ -2808,7 +3055,7 @@ RTLIL::Const RTLIL::SigSpec::as_const() const cover("kernel.rtlil.sigspec.as_const"); pack(); - log_assert(is_fully_const() && SIZE(chunks_) <= 1); + log_assert(is_fully_const() && GetSize(chunks_) <= 1); if (width_) return chunks_[0].data; return RTLIL::Const(); @@ -2867,6 +3114,18 @@ std::set RTLIL::SigSpec::to_sigbit_set() const return sigbits; } +pool RTLIL::SigSpec::to_sigbit_pool() const +{ + cover("kernel.rtlil.sigspec.to_sigbit_pool"); + + pack(); + pool sigbits; + for (auto &c : chunks_) + for (int i = 0; i < c.width; i++) + sigbits.insert(RTLIL::SigBit(c, i)); + return sigbits; +} + std::vector RTLIL::SigSpec::to_sigbit_vector() const { cover("kernel.rtlil.sigspec.to_sigbit_vector"); @@ -2891,6 +3150,22 @@ std::map RTLIL::SigSpec::to_sigbit_map(const RTLIL return new_map; } +dict RTLIL::SigSpec::to_sigbit_dict(const RTLIL::SigSpec &other) const +{ + cover("kernel.rtlil.sigspec.to_sigbit_dict"); + + unpack(); + other.unpack(); + + log_assert(width_ == other.width_); + + dict new_map; + for (int i = 0; i < width_; i++) + new_map[bits_[i]] = other.bits_[i]; + + return new_map; +} + RTLIL::SigBit RTLIL::SigSpec::to_single_sigbit() const { cover("kernel.rtlil.sigspec.to_single_sigbit"); @@ -2934,7 +3209,7 @@ bool RTLIL::SigSpec::parse(RTLIL::SigSpec &sig, RTLIL::Module *module, std::stri if (netname.size() == 0) continue; - if ('0' <= netname[0] && netname[0] <= '9') { + if (('0' <= netname[0] && netname[0] <= '9') || netname[0] == '\'') { cover("kernel.rtlil.sigspec.parse.const"); AST::get_line_num = sigspec_parse_get_dummy_line_num; AST::AstNode *ast = VERILOG_FRONTEND::const2ast(netname); @@ -2979,7 +3254,10 @@ bool RTLIL::SigSpec::parse(RTLIL::SigSpec &sig, RTLIL::Module *module, std::stri sigspec_parse_split(index_tokens, indices.substr(1, indices.size()-2), ':'); if (index_tokens.size() == 1) { cover("kernel.rtlil.sigspec.parse.bit_sel"); - sig.append(RTLIL::SigSpec(wire, atoi(index_tokens.at(0).c_str()))); + int a = atoi(index_tokens.at(0).c_str()); + if (a < 0 || a >= wire->width) + return false; + sig.append(RTLIL::SigSpec(wire, a)); } else { cover("kernel.rtlil.sigspec.parse.part_sel"); int a = atoi(index_tokens.at(0).c_str()); @@ -2988,6 +3266,10 @@ bool RTLIL::SigSpec::parse(RTLIL::SigSpec &sig, RTLIL::Module *module, std::stri int tmp = a; a = b, b = tmp; } + if (a < 0 || a >= wire->width) + return false; + if (b < 0 || b >= wire->width) + return false; sig.append(RTLIL::SigSpec(wire, a, b-a+1)); } } else @@ -3033,7 +3315,7 @@ bool RTLIL::SigSpec::parse_rhs(const RTLIL::SigSpec &lhs, RTLIL::SigSpec &sig, R if (lhs.chunks_.size() == 1) { char *p = (char*)str.c_str(), *endptr; - long long int val = strtoll(p, &endptr, 10); + long int val = strtol(p, &endptr, 10); if (endptr && endptr != p && *endptr == 0) { sig = RTLIL::SigSpec(val, lhs.width_); cover("kernel.rtlil.sigspec.parse.rhs_dec"); diff --git a/kernel/rtlil.h b/kernel/rtlil.h index a0ae8f08..1d0008f9 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -76,18 +76,15 @@ namespace RTLIL { // the global id string cache - struct char_ptr_cmp { - bool operator()(const char *a, const char *b) const { - for (int i = 0; a[i] || b[i]; i++) - if (a[i] != b[i]) - return a[i] < b[i]; - return false; - } - }; + static struct destruct_guard_t { + bool ok; // POD, will be initialized to zero + destruct_guard_t() { ok = true; } + ~destruct_guard_t() { ok = false; } + } destruct_guard; static std::vector global_refcount_storage_; static std::vector global_id_storage_; - static std::map global_id_index_; + static dict global_id_index_; static std::vector global_free_idx_list_; static inline int get_reference(int idx) @@ -98,6 +95,8 @@ namespace RTLIL static inline int get_reference(const char *p) { + log_assert(destruct_guard.ok); + if (p[0]) { log_assert(p[1] != 0); log_assert(p[0] == '$' || p[0] == '\\'); @@ -121,16 +120,38 @@ namespace RTLIL global_id_storage_.at(idx) = strdup(p); global_id_index_[global_id_storage_.at(idx)] = idx; global_refcount_storage_.at(idx)++; + + // Avoid Create->Delete->Create pattern + static IdString last_created_id; + put_reference(last_created_id.index_); + last_created_id.index_ = idx; + get_reference(last_created_id.index_); + + if (yosys_xtrace) { + log("#X# New IdString '%s' with index %d.\n", p, idx); + log_backtrace("-X- ", yosys_xtrace-1); + } + return idx; } static inline void put_reference(int idx) { + // put_reference() may be called from destructors after the destructor of + // global_refcount_storage_ has been run. in this case we simply do nothing. + if (!destruct_guard.ok) + return; + log_assert(global_refcount_storage_.at(idx) > 0); if (--global_refcount_storage_.at(idx) != 0) return; + if (yosys_xtrace) { + log("#X# Removed IdString '%s' with index %d.\n", global_id_storage_.at(idx), idx); + log_backtrace("-X- ", yosys_xtrace-1); + } + global_id_index_.erase(global_id_storage_.at(idx)); free(global_id_storage_.at(idx)); global_id_storage_.at(idx) = nullptr; @@ -211,12 +232,16 @@ namespace RTLIL *this = IdString(); } + unsigned int hash() const { + return index_; + } + // The following is a helper key_compare class. Instead of for example std::set // use std::set> if the order of cells in the // set has an influence on the algorithm. template struct compare_ptr_by_name { - bool operator()(const T *a, const T *b) { + bool operator()(const T *a, const T *b) const { return (a == nullptr || b == nullptr) ? (a < b) : (a->name < b->name); } }; @@ -225,14 +250,14 @@ namespace RTLIL // of cell types). the following functions helps with that. template - bool in(T first, Args... rest) { + bool in(T first, Args... rest) const { return in(first) || in(rest...); } - bool in(IdString rhs) { return *this == rhs; } - bool in(const char *rhs) { return *this == rhs; } - bool in(const std::string &rhs) { return *this == rhs; } - bool in(const std::set &rhs) { return rhs.count(*this) != 0; } + bool in(IdString rhs) const { return *this == rhs; } + bool in(const char *rhs) const { return *this == rhs; } + bool in(const std::string &rhs) const { return *this == rhs; } + bool in(const pool &rhs) const { return rhs.count(*this) != 0; } }; static inline std::string escape_id(std::string str) { @@ -242,9 +267,15 @@ namespace RTLIL } static inline std::string unescape_id(std::string str) { - if (str.size() > 1 && str[0] == '\\' && str[1] != '$') - return str.substr(1); - return str; + if (str.size() < 2) + return str; + if (str[0] != '\\') + return str; + if (str[1] == '$' || str[1] == '\\') + return str; + if (str[1] >= '0' && str[1] <= '9') + return str; + return str.substr(1); } static inline std::string unescape_id(RTLIL::IdString str) { @@ -323,8 +354,8 @@ namespace RTLIL template struct ObjIterator { - typename std::map::iterator it; - std::map *list_p; + typename dict::iterator it; + dict *list_p; int *refcount_p; ObjIterator() : list_p(nullptr), refcount_p(nullptr) { @@ -388,7 +419,7 @@ namespace RTLIL template struct ObjRange { - std::map *list_p; + dict *list_p; int *refcount_p; ObjRange(decltype(list_p) list_p, int *refcount_p) : list_p(list_p), refcount_p(refcount_p) { } @@ -399,8 +430,8 @@ namespace RTLIL return list_p->size(); } - operator std::set() const { - std::set result; + operator pool() const { + pool result; for (auto &it : *list_p) result.insert(it.second); return result; @@ -414,7 +445,7 @@ namespace RTLIL return result; } - std::set to_set() const { return *this; } + pool to_pool() const { return *this; } std::vector to_vector() const { return *this; } }; }; @@ -438,17 +469,249 @@ struct RTLIL::Const bool as_bool() const; int as_int(bool is_signed = false) const; std::string as_string() const; + static Const from_string(std::string str); std::string decode_string() const; inline int size() const { return bits.size(); } + + inline unsigned int hash() const { + unsigned int h = mkhash_init; + for (auto b : bits) + mkhash(h, b); + return h; + } +}; + +struct RTLIL::SigChunk +{ + RTLIL::Wire *wire; + std::vector data; // only used if wire == NULL, LSB at index 0 + int width, offset; + + SigChunk(); + SigChunk(const RTLIL::Const &value); + SigChunk(RTLIL::Wire *wire); + SigChunk(RTLIL::Wire *wire, int offset, int width = 1); + SigChunk(const std::string &str); + SigChunk(int val, int width = 32); + SigChunk(RTLIL::State bit, int width = 1); + SigChunk(RTLIL::SigBit bit); + + RTLIL::SigChunk extract(int offset, int length) const; + + bool operator <(const RTLIL::SigChunk &other) const; + bool operator ==(const RTLIL::SigChunk &other) const; + bool operator !=(const RTLIL::SigChunk &other) const; +}; + +struct RTLIL::SigBit +{ + RTLIL::Wire *wire; + union { + RTLIL::State data; // used if wire == NULL + int offset; // used if wire != NULL + }; + + SigBit(); + SigBit(RTLIL::State bit); + SigBit(bool bit); + SigBit(RTLIL::Wire *wire); + SigBit(RTLIL::Wire *wire, int offset); + SigBit(const RTLIL::SigChunk &chunk); + SigBit(const RTLIL::SigChunk &chunk, int index); + SigBit(const RTLIL::SigSpec &sig); + + bool operator <(const RTLIL::SigBit &other) const; + bool operator ==(const RTLIL::SigBit &other) const; + bool operator !=(const RTLIL::SigBit &other) const; + unsigned int hash() const; +}; + +struct RTLIL::SigSpecIterator : public std::iterator +{ + RTLIL::SigSpec *sig_p; + int index; + + inline RTLIL::SigBit &operator*() const; + inline bool operator!=(const RTLIL::SigSpecIterator &other) const { return index != other.index; } + inline bool operator==(const RTLIL::SigSpecIterator &other) const { return index == other.index; } + inline void operator++() { index++; } +}; + +struct RTLIL::SigSpecConstIterator : public std::iterator +{ + const RTLIL::SigSpec *sig_p; + int index; + + inline const RTLIL::SigBit &operator*() const; + inline bool operator!=(const RTLIL::SigSpecConstIterator &other) const { return index != other.index; } + inline bool operator==(const RTLIL::SigSpecIterator &other) const { return index == other.index; } + inline void operator++() { index++; } +}; + +struct RTLIL::SigSpec +{ +private: + int width_; + unsigned long hash_; + std::vector chunks_; // LSB at index 0 + std::vector bits_; // LSB at index 0 + + void pack() const; + void unpack() const; + void updhash() const; + + inline bool packed() const { + return bits_.empty(); + } + + inline void inline_unpack() const { + if (!chunks_.empty()) + unpack(); + } + +public: + SigSpec(); + SigSpec(const RTLIL::SigSpec &other); + SigSpec(std::initializer_list parts); + const RTLIL::SigSpec &operator=(const RTLIL::SigSpec &other); + + SigSpec(const RTLIL::Const &value); + SigSpec(const RTLIL::SigChunk &chunk); + SigSpec(RTLIL::Wire *wire); + SigSpec(RTLIL::Wire *wire, int offset, int width = 1); + SigSpec(const std::string &str); + SigSpec(int val, int width = 32); + SigSpec(RTLIL::State bit, int width = 1); + SigSpec(RTLIL::SigBit bit, int width = 1); + SigSpec(std::vector chunks); + SigSpec(std::vector bits); + SigSpec(pool bits); + SigSpec(std::set bits); + SigSpec(bool bit); + + SigSpec(RTLIL::SigSpec &&other) { + width_ = other.width_; + hash_ = other.hash_; + chunks_ = std::move(other.chunks_); + bits_ = std::move(other.bits_); + } + + const RTLIL::SigSpec &operator=(RTLIL::SigSpec &&other) { + width_ = other.width_; + hash_ = other.hash_; + chunks_ = std::move(other.chunks_); + bits_ = std::move(other.bits_); + return *this; + } + + size_t get_hash() const { + if (!hash_) hash(); + return hash_; + } + + inline const std::vector &chunks() const { pack(); return chunks_; } + inline const std::vector &bits() const { inline_unpack(); return bits_; } + + inline int size() const { return width_; } + inline bool empty() const { return width_ == 0; } + + inline RTLIL::SigBit &operator[](int index) { inline_unpack(); return bits_.at(index); } + inline const RTLIL::SigBit &operator[](int index) const { inline_unpack(); return bits_.at(index); } + + inline RTLIL::SigSpecIterator begin() { RTLIL::SigSpecIterator it; it.sig_p = this; it.index = 0; return it; } + inline RTLIL::SigSpecIterator end() { RTLIL::SigSpecIterator it; it.sig_p = this; it.index = width_; return it; } + + inline RTLIL::SigSpecConstIterator begin() const { RTLIL::SigSpecConstIterator it; it.sig_p = this; it.index = 0; return it; } + inline RTLIL::SigSpecConstIterator end() const { RTLIL::SigSpecConstIterator it; it.sig_p = this; it.index = width_; return it; } + + void sort(); + void sort_and_unify(); + + void replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with); + void replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with, RTLIL::SigSpec *other) const; + + void replace(const dict &rules); + void replace(const dict &rules, RTLIL::SigSpec *other) const; + + void replace(const std::map &rules); + void replace(const std::map &rules, RTLIL::SigSpec *other) const; + + void replace(int offset, const RTLIL::SigSpec &with); + + void remove(const RTLIL::SigSpec &pattern); + void remove(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other) const; + void remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other); + + void remove(const pool &pattern); + void remove(const pool &pattern, RTLIL::SigSpec *other) const; + void remove2(const pool &pattern, RTLIL::SigSpec *other); + + void remove(int offset, int length = 1); + void remove_const(); + + RTLIL::SigSpec extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other = NULL) const; + RTLIL::SigSpec extract(const pool &pattern, const RTLIL::SigSpec *other = NULL) const; + RTLIL::SigSpec extract(int offset, int length = 1) const; + + void append(const RTLIL::SigSpec &signal); + void append_bit(const RTLIL::SigBit &bit); + + void extend_u0(int width, bool is_signed = false); + + RTLIL::SigSpec repeat(int num) const; + + bool operator <(const RTLIL::SigSpec &other) const; + bool operator ==(const RTLIL::SigSpec &other) const; + inline bool operator !=(const RTLIL::SigSpec &other) const { return !(*this == other); } + + bool is_wire() const; + bool is_chunk() const; + + bool is_fully_const() const; + bool is_fully_def() const; + bool is_fully_undef() const; + bool has_const() const; + bool has_marked_bits() const; + + bool as_bool() const; + int as_int(bool is_signed = false) const; + std::string as_string() const; + RTLIL::Const as_const() const; + RTLIL::Wire *as_wire() const; + RTLIL::SigChunk as_chunk() const; + + bool match(std::string pattern) const; + + std::set to_sigbit_set() const; + pool to_sigbit_pool() const; + std::vector to_sigbit_vector() const; + std::map to_sigbit_map(const RTLIL::SigSpec &other) const; + dict 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); + static bool parse_rhs(const RTLIL::SigSpec &lhs, RTLIL::SigSpec &sig, RTLIL::Module *module, std::string str); + + operator std::vector() const { return chunks(); } + operator std::vector() const { return bits(); } + + unsigned int hash() const { if (!hash_) updhash(); return hash_; }; + +#ifndef NDEBUG + void check() const; +#else + void check() const { } +#endif }; struct RTLIL::Selection { bool full_selection; - std::set selected_modules; - std::map> selected_members; + pool selected_modules; + dict> selected_members; Selection(bool full = true) : full_selection(full) { } @@ -476,6 +739,15 @@ struct RTLIL::Selection struct RTLIL::Monitor { + unsigned int hashidx_; + unsigned int hash() const { return hashidx_; } + + Monitor() { + static unsigned int hashidx_count = 123456789; + hashidx_count = mkhash_xorshift(hashidx_count); + hashidx_ = hashidx_count; + } + virtual ~Monitor() { } virtual void notify_module_add(RTLIL::Module*) { } virtual void notify_module_del(RTLIL::Module*) { } @@ -487,14 +759,17 @@ struct RTLIL::Monitor struct RTLIL::Design { - std::set monitors; - std::map scratchpad; + unsigned int hashidx_; + unsigned int hash() const { return hashidx_; } + + pool monitors; + dict scratchpad; int refcount_modules_; - std::map modules_; + dict modules_; std::vector selection_stack; - std::map selection_vars; + dict selection_vars; std::string selected_active_module; Design(); @@ -521,6 +796,7 @@ struct RTLIL::Design bool scratchpad_get_bool(std::string varname, bool default_value = false) const; std::string scratchpad_get_string(std::string varname, std::string default_value = std::string()) const; + void sort(); void check(); void optimize(); @@ -531,6 +807,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; } @@ -556,7 +840,7 @@ struct RTLIL::Design }; #define RTLIL_ATTRIBUTE_MEMBERS \ - std::map attributes; \ + dict attributes; \ void set_bool_attribute(RTLIL::IdString id) { \ attributes[id] = RTLIL::Const(1); \ } \ @@ -568,31 +852,36 @@ struct RTLIL::Design struct RTLIL::Module { + unsigned int hashidx_; + unsigned int hash() const { return hashidx_; } + protected: void add(RTLIL::Wire *wire); void add(RTLIL::Cell *cell); public: RTLIL::Design *design; - std::set monitors; + pool monitors; int refcount_wires_; int refcount_cells_; - std::map wires_; - std::map cells_; + dict wires_; + dict cells_; std::vector connections_; RTLIL::IdString name; - std::set avail_parameters; - std::map memories; - std::map processes; + pool avail_parameters; + dict memories; + dict processes; RTLIL_ATTRIBUTE_MEMBERS Module(); virtual ~Module(); - virtual RTLIL::IdString derive(RTLIL::Design *design, std::map parameters); + virtual RTLIL::IdString derive(RTLIL::Design *design, dict parameters); virtual size_t count_id(RTLIL::IdString id); + + virtual void sort(); virtual void check(); virtual void optimize(); @@ -628,7 +917,7 @@ public: RTLIL::ObjRange cells() { return RTLIL::ObjRange(&cells_, &refcount_cells_); } // Removing wires is expensive. If you have to remove wires, remove them all at once. - void remove(const std::set &wires); + void remove(const pool &wires); void remove(RTLIL::Cell *cell); void rename(RTLIL::Wire *wire, RTLIL::IdString new_name); @@ -698,9 +987,11 @@ public: 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* 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); RTLIL::Cell* addSr (RTLIL::IdString name, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, RTLIL::SigSpec sig_q, bool set_polarity = true, bool clr_polarity = true); RTLIL::Cell* addDff (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true); + RTLIL::Cell* addDffe (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true, bool en_polarity = true); RTLIL::Cell* addDffsr (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true, bool set_polarity = true, bool clr_polarity = true); RTLIL::Cell* addAdff (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_arst, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, @@ -723,6 +1014,7 @@ public: RTLIL::Cell* addOai4Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c, RTLIL::SigBit sig_d, RTLIL::SigBit sig_y); RTLIL::Cell* addDffGate (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true); + RTLIL::Cell* addDffeGate (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true, bool en_polarity = true); RTLIL::Cell* addDffsrGate (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true, bool set_polarity = true, bool clr_polarity = true); RTLIL::Cell* addAdffGate (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_arst, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, @@ -795,6 +1087,9 @@ public: struct RTLIL::Wire { + unsigned int hashidx_; + unsigned int hash() const { return hashidx_; } + protected: // use module->addWire() and module->remove() to create or destroy wires friend struct RTLIL::Module; @@ -815,6 +1110,9 @@ public: struct RTLIL::Memory { + unsigned int hashidx_; + unsigned int hash() const { return hashidx_; } + Memory(); RTLIL::IdString name; @@ -824,6 +1122,9 @@ struct RTLIL::Memory struct RTLIL::Cell { + unsigned int hashidx_; + unsigned int hash() const { return hashidx_; } + protected: // use module->addCell() and module->remove() to create or destroy cells friend struct RTLIL::Module; @@ -837,8 +1138,8 @@ public: RTLIL::Module *module; RTLIL::IdString name; RTLIL::IdString type; - std::map connections_; - std::map parameters; + dict connections_; + dict parameters; RTLIL_ATTRIBUTE_MEMBERS // access cell ports @@ -846,7 +1147,12 @@ public: void unsetPort(RTLIL::IdString portname); void setPort(RTLIL::IdString portname, RTLIL::SigSpec signal); const RTLIL::SigSpec &getPort(RTLIL::IdString portname) const; - const std::map &connections() const; + const dict &connections() const; + + // information about cell ports + bool known() const; + bool input(RTLIL::IdString portname) const; + bool output(RTLIL::IdString portname) const; // access cell parameters bool hasParam(RTLIL::IdString paramname) const; @@ -854,242 +1160,18 @@ public: void setParam(RTLIL::IdString paramname, RTLIL::Const value); const RTLIL::Const &getParam(RTLIL::IdString paramname) const; + void sort(); void check(); void fixup_parameters(bool set_a_signed = false, bool set_b_signed = false); + bool has_keep_attr() const { + return get_bool_attribute("\\keep") || (module && module->design && module->design->module(type) && + module->design->module(type)->get_bool_attribute("\\keep")); + } + template void rewrite_sigspecs(T functor); }; -struct RTLIL::SigChunk -{ - RTLIL::Wire *wire; - std::vector data; // only used if wire == NULL, LSB at index 0 - int width, offset; - - SigChunk(); - SigChunk(const RTLIL::Const &value); - SigChunk(RTLIL::Wire *wire); - SigChunk(RTLIL::Wire *wire, int offset, int width = 1); - SigChunk(const std::string &str); - SigChunk(int val, int width = 32); - SigChunk(RTLIL::State bit, int width = 1); - SigChunk(RTLIL::SigBit bit); - - RTLIL::SigChunk extract(int offset, int length) const; - - bool operator <(const RTLIL::SigChunk &other) const; - bool operator ==(const RTLIL::SigChunk &other) const; - bool operator !=(const RTLIL::SigChunk &other) const; -}; - -struct RTLIL::SigBit -{ - RTLIL::Wire *wire; - union { - RTLIL::State data; // used if wire == NULL - int offset; // used if wire != NULL - }; - - SigBit() : wire(NULL), data(RTLIL::State::S0) { } - SigBit(RTLIL::State bit) : wire(NULL), data(bit) { } - SigBit(RTLIL::Wire *wire) : wire(wire), offset(0) { log_assert(wire && wire->width == 1); } - SigBit(RTLIL::Wire *wire, int offset) : wire(wire), offset(offset) { log_assert(wire); } - SigBit(const RTLIL::SigChunk &chunk) : wire(chunk.wire) { log_assert(chunk.width == 1); if (wire) offset = chunk.offset; else data = chunk.data[0]; } - SigBit(const RTLIL::SigChunk &chunk, int index) : wire(chunk.wire) { if (wire) offset = chunk.offset + index; else data = chunk.data[index]; } - SigBit(const RTLIL::SigSpec &sig); - - bool operator <(const RTLIL::SigBit &other) const { - if (wire == other.wire) - return wire ? (offset < other.offset) : (data < other.data); - if (wire != nullptr && other.wire != nullptr) - return wire->name < other.wire->name; - return wire < other.wire; - } - - bool operator ==(const RTLIL::SigBit &other) const { - return (wire == other.wire) && (wire ? (offset == other.offset) : (data == other.data)); - } - - bool operator !=(const RTLIL::SigBit &other) const { - return (wire != other.wire) || (wire ? (offset != other.offset) : (data != other.data)); - } -}; - -struct RTLIL::SigSpecIterator -{ - RTLIL::SigSpec *sig_p; - int index; - - inline RTLIL::SigBit &operator*() const; - inline bool operator!=(const RTLIL::SigSpecIterator &other) const { return index != other.index; } - inline void operator++() { index++; } -}; - -struct RTLIL::SigSpecConstIterator -{ - const RTLIL::SigSpec *sig_p; - int index; - - inline const RTLIL::SigBit &operator*() const; - inline bool operator!=(const RTLIL::SigSpecConstIterator &other) const { return index != other.index; } - inline void operator++() { index++; } -}; - -struct RTLIL::SigSpec -{ -private: - int width_; - unsigned long hash_; - std::vector chunks_; // LSB at index 0 - std::vector bits_; // LSB at index 0 - - void pack() const; - void unpack() const; - void hash() const; - - inline bool packed() const { - return bits_.empty(); - } - - inline void inline_unpack() const { - if (!chunks_.empty()) - unpack(); - } - -public: - SigSpec(); - SigSpec(const RTLIL::SigSpec &other); - SigSpec(std::initializer_list parts); - const RTLIL::SigSpec &operator=(const RTLIL::SigSpec &other); - - SigSpec(const RTLIL::Const &value); - SigSpec(const RTLIL::SigChunk &chunk); - SigSpec(RTLIL::Wire *wire); - SigSpec(RTLIL::Wire *wire, int offset, int width = 1); - SigSpec(const std::string &str); - SigSpec(int val, int width = 32); - SigSpec(RTLIL::State bit, int width = 1); - SigSpec(RTLIL::SigBit bit, int width = 1); - SigSpec(std::vector chunks); - SigSpec(std::vector bits); - SigSpec(std::set bits); - - SigSpec(RTLIL::SigSpec &&other) { - width_ = other.width_; - hash_ = other.hash_; - chunks_ = std::move(other.chunks_); - bits_ = std::move(other.bits_); - } - - const RTLIL::SigSpec &operator=(RTLIL::SigSpec &&other) { - width_ = other.width_; - hash_ = other.hash_; - chunks_ = std::move(other.chunks_); - bits_ = std::move(other.bits_); - return *this; - } - - inline const std::vector &chunks() const { pack(); return chunks_; } - inline const std::vector &bits() const { inline_unpack(); return bits_; } - - inline int size() const { return width_; } - - inline RTLIL::SigBit &operator[](int index) { inline_unpack(); return bits_.at(index); } - inline const RTLIL::SigBit &operator[](int index) const { inline_unpack(); return bits_.at(index); } - - inline RTLIL::SigSpecIterator begin() { RTLIL::SigSpecIterator it; it.sig_p = this; it.index = 0; return it; } - inline RTLIL::SigSpecIterator end() { RTLIL::SigSpecIterator it; it.sig_p = this; it.index = width_; return it; } - - inline RTLIL::SigSpecConstIterator begin() const { RTLIL::SigSpecConstIterator it; it.sig_p = this; it.index = 0; return it; } - inline RTLIL::SigSpecConstIterator end() const { RTLIL::SigSpecConstIterator it; it.sig_p = this; it.index = width_; return it; } - - void sort(); - void sort_and_unify(); - - void replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with); - void replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec &with, RTLIL::SigSpec *other) const; - - void replace(const std::map &rules); - void replace(const std::map &rules, RTLIL::SigSpec *other) const; - - void replace(int offset, const RTLIL::SigSpec &with); - - void remove(const RTLIL::SigSpec &pattern); - void remove(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other) const; - void remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *other); - - void remove(const std::set &pattern); - void remove(const std::set &pattern, RTLIL::SigSpec *other) const; - void remove2(const std::set &pattern, RTLIL::SigSpec *other); - - void remove(int offset, int length = 1); - void remove_const(); - - RTLIL::SigSpec extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other = NULL) const; - RTLIL::SigSpec extract(const std::set &pattern, const RTLIL::SigSpec *other = NULL) const; - RTLIL::SigSpec extract(int offset, int length = 1) const; - - void append(const RTLIL::SigSpec &signal); - void append_bit(const RTLIL::SigBit &bit); - - void extend(int width, bool is_signed = false); - void extend_u0(int width, bool is_signed = false); - - RTLIL::SigSpec repeat(int num) const; - - bool operator <(const RTLIL::SigSpec &other) const; - bool operator ==(const RTLIL::SigSpec &other) const; - inline bool operator !=(const RTLIL::SigSpec &other) const { return !(*this == other); } - - bool is_wire() const; - bool is_chunk() const; - - bool is_fully_const() const; - bool is_fully_def() const; - bool is_fully_undef() const; - bool has_marked_bits() const; - - bool as_bool() const; - int as_int(bool is_signed = false) const; - std::string as_string() const; - RTLIL::Const as_const() const; - RTLIL::Wire *as_wire() const; - RTLIL::SigChunk as_chunk() const; - - bool match(std::string pattern) const; - - std::set to_sigbit_set() const; - std::vector to_sigbit_vector() const; - std::map to_sigbit_map(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); - static bool parse_rhs(const RTLIL::SigSpec &lhs, RTLIL::SigSpec &sig, RTLIL::Module *module, std::string str); - - operator std::vector() const { return chunks(); } - operator std::vector() const { return bits(); } - -#ifndef NDEBUG - void check() const; -#else - inline void check() const { } -#endif -}; - -inline RTLIL::SigBit &RTLIL::SigSpecIterator::operator*() const { - return (*sig_p)[index]; -} - -inline const RTLIL::SigBit &RTLIL::SigSpecConstIterator::operator*() const { - return (*sig_p)[index]; -} - -inline RTLIL::SigBit::SigBit(const RTLIL::SigSpec &sig) { - log_assert(sig.size() == 1 && sig.chunks().size() == 1); - *this = SigBit(sig.chunks().front()); -} - struct RTLIL::CaseRule { std::vector compare; @@ -1138,6 +1220,50 @@ struct RTLIL::Process RTLIL::Process *clone() const; }; + +inline RTLIL::SigBit::SigBit() : wire(NULL), data(RTLIL::State::S0) { } +inline RTLIL::SigBit::SigBit(RTLIL::State bit) : wire(NULL), data(bit) { } +inline RTLIL::SigBit::SigBit(bool bit) : wire(NULL), data(bit ? RTLIL::S1 : RTLIL::S0) { } +inline RTLIL::SigBit::SigBit(RTLIL::Wire *wire) : wire(wire), offset(0) { log_assert(wire && wire->width == 1); } +inline RTLIL::SigBit::SigBit(RTLIL::Wire *wire, int offset) : wire(wire), offset(offset) { log_assert(wire != nullptr); } +inline RTLIL::SigBit::SigBit(const RTLIL::SigChunk &chunk) : wire(chunk.wire) { log_assert(chunk.width == 1); if (wire) offset = chunk.offset; else data = chunk.data[0]; } +inline RTLIL::SigBit::SigBit(const RTLIL::SigChunk &chunk, int index) : wire(chunk.wire) { if (wire) offset = chunk.offset + index; else data = chunk.data[index]; } + +inline bool RTLIL::SigBit::operator<(const RTLIL::SigBit &other) const { + if (wire == other.wire) + return wire ? (offset < other.offset) : (data < other.data); + if (wire != nullptr && other.wire != nullptr) + return wire->name < other.wire->name; + return wire < other.wire; +} + +inline bool RTLIL::SigBit::operator==(const RTLIL::SigBit &other) const { + return (wire == other.wire) && (wire ? (offset == other.offset) : (data == other.data)); +} + +inline bool RTLIL::SigBit::operator!=(const RTLIL::SigBit &other) const { + return (wire != other.wire) || (wire ? (offset != other.offset) : (data != other.data)); +} + +inline unsigned int RTLIL::SigBit::hash() const { + if (wire) + return mkhash_add(wire->name.hash(), offset); + return data; +} + +inline RTLIL::SigBit &RTLIL::SigSpecIterator::operator*() const { + return (*sig_p)[index]; +} + +inline const RTLIL::SigBit &RTLIL::SigSpecConstIterator::operator*() const { + return (*sig_p)[index]; +} + +inline RTLIL::SigBit::SigBit(const RTLIL::SigSpec &sig) { + log_assert(sig.size() == 1 && sig.chunks().size() == 1); + *this = SigBit(sig.chunks().front()); +} + template void RTLIL::Module::rewrite_sigspecs(T functor) { diff --git a/kernel/satgen.h b/kernel/satgen.h index 692c6e7f..719b0a83 100644 --- a/kernel/satgen.h +++ b/kernel/satgen.h @@ -26,7 +26,40 @@ #include "kernel/macc.h" #include "libs/ezsat/ezminisat.h" -typedef ezMiniSAT ezDefaultSAT; + +YOSYS_NAMESPACE_BEGIN + +// 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 { + ezSatPtr() : unique_ptr(yosys_satsolver->create()) { } +}; struct SatGen { @@ -35,6 +68,8 @@ struct SatGen std::string prefix; SigPool initial_state; std::map asserts_a, asserts_en; + std::map assumes_a, assumes_en; + std::map> imported_signals; bool ignore_div_by_zero; bool model_undef; @@ -49,23 +84,24 @@ struct SatGen this->prefix = prefix; } - std::vector importSigSpecWorker(RTLIL::SigSpec &sig, std::string &pf, bool undef_mode, bool dup_undef) + std::vector importSigSpecWorker(RTLIL::SigSpec sig, std::string &pf, bool undef_mode, bool dup_undef) { log_assert(!undef_mode || model_undef); sigmap->apply(sig); std::vector vec; - vec.reserve(SIZE(sig)); + vec.reserve(GetSize(sig)); for (auto &bit : sig) if (bit.wire == NULL) { if (model_undef && dup_undef && bit == RTLIL::State::Sx) vec.push_back(ez->frozen_literal()); else - vec.push_back(bit == (undef_mode ? RTLIL::State::Sx : RTLIL::State::S1) ? ez->TRUE : ez->FALSE); + vec.push_back(bit == (undef_mode ? RTLIL::State::Sx : RTLIL::State::S1) ? ez->CONST_TRUE : ez->CONST_FALSE); } else { - std::string name = pf + stringf(bit.wire->width == 1 ? "%s" : "%s [%d]", RTLIL::id2cstr(bit.wire->name), bit.offset); + std::string name = pf + (bit.wire->width == 1 ? stringf("%s", log_id(bit.wire)) : stringf("%s [%d]", log_id(bit.wire->name), bit.offset)); vec.push_back(ez->frozen_literal(name)); + imported_signals[pf][bit] = vec.back(); } return vec; } @@ -91,6 +127,34 @@ struct SatGen return importSigSpecWorker(sig, pf, true, false); } + int importSigBit(RTLIL::SigBit bit, int timestep = -1) + { + log_assert(timestep != 0); + std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep)); + return importSigSpecWorker(bit, pf, false, false).front(); + } + + int importDefSigBit(RTLIL::SigBit bit, int timestep = -1) + { + log_assert(timestep != 0); + std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep)); + return importSigSpecWorker(bit, pf, false, true).front(); + } + + int importUndefSigBit(RTLIL::SigBit bit, int timestep = -1) + { + log_assert(timestep != 0); + std::string pf = "undef:" + prefix + (timestep == -1 ? "" : stringf("@%d:", timestep)); + return importSigSpecWorker(bit, pf, true, false).front(); + } + + bool importedSigBit(RTLIL::SigBit bit, int timestep = -1) + { + log_assert(timestep != 0); + std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep)); + return imported_signals[pf].count(bit) != 0; + } + void getAsserts(RTLIL::SigSpec &sig_a, RTLIL::SigSpec &sig_en, int timestep = -1) { std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep)); @@ -98,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 check_bits, enable_bits; @@ -112,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 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) @@ -141,9 +226,9 @@ struct SatGen if (!forced_signed && cell->parameters.count("\\A_SIGNED") > 0 && cell->parameters.count("\\B_SIGNED") > 0) is_signed = cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool(); while (vec_a.size() < vec_b.size() || vec_a.size() < y_width) - vec_a.push_back(is_signed && vec_a.size() > 0 ? vec_a.back() : ez->FALSE); + vec_a.push_back(is_signed && vec_a.size() > 0 ? vec_a.back() : ez->CONST_FALSE); while (vec_b.size() < vec_a.size() || vec_b.size() < y_width) - vec_b.push_back(is_signed && vec_b.size() > 0 ? vec_b.back() : ez->FALSE); + vec_b.push_back(is_signed && vec_b.size() > 0 ? vec_b.back() : ez->CONST_FALSE); } void extendSignalWidth(std::vector &vec_a, std::vector &vec_b, std::vector &vec_y, RTLIL::Cell *cell, bool forced_signed = false) @@ -157,7 +242,7 @@ struct SatGen { bool is_signed = forced_signed || (cell->parameters.count("\\A_SIGNED") > 0 && cell->parameters["\\A_SIGNED"].as_bool()); while (vec_a.size() < vec_y.size()) - vec_a.push_back(is_signed && vec_a.size() > 0 ? vec_a.back() : ez->FALSE); + vec_a.push_back(is_signed && vec_a.size() > 0 ? vec_a.back() : ez->CONST_FALSE); while (vec_y.size() < vec_a.size()) vec_y.push_back(ez->literal()); } @@ -207,7 +292,7 @@ struct SatGen if (is_arith_compare) { for (size_t i = 1; i < undef_y.size(); i++) - ez->SET(ez->FALSE, undef_y.at(i)); + ez->SET(ez->CONST_FALSE, undef_y.at(i)); ez->SET(undef_y_bit, undef_y.at(0)); } else { std::vector undef_y_bits(undef_y.size(), undef_y_bit); @@ -288,7 +373,7 @@ struct SatGen int a = importDefSigSpec(cell->getPort("\\A"), timestep).at(0); int b = importDefSigSpec(cell->getPort("\\B"), timestep).at(0); int c = importDefSigSpec(cell->getPort("\\C"), timestep).at(0); - int d = three_mode ? (aoi_mode ? ez->TRUE : ez->FALSE) : importDefSigSpec(cell->getPort("\\D"), timestep).at(0); + int d = three_mode ? (aoi_mode ? ez->CONST_TRUE : ez->CONST_FALSE) : importDefSigSpec(cell->getPort("\\D"), timestep).at(0); int y = importDefSigSpec(cell->getPort("\\Y"), timestep).at(0); int yy = model_undef ? ez->literal() : y; @@ -302,7 +387,7 @@ struct SatGen int undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep).at(0); int undef_b = importUndefSigSpec(cell->getPort("\\B"), timestep).at(0); int undef_c = importUndefSigSpec(cell->getPort("\\C"), timestep).at(0); - int undef_d = three_mode ? ez->FALSE : importUndefSigSpec(cell->getPort("\\D"), timestep).at(0); + int undef_d = three_mode ? ez->CONST_FALSE : importUndefSigSpec(cell->getPort("\\D"), timestep).at(0); int undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep).at(0); if (aoi_mode) @@ -414,14 +499,14 @@ struct SatGen std::vector undef_s = importUndefSigSpec(cell->getPort("\\S"), timestep); std::vector undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep); - int maybe_one_hot = ez->FALSE; - int maybe_many_hot = ez->FALSE; + int maybe_one_hot = ez->CONST_FALSE; + int maybe_many_hot = ez->CONST_FALSE; - int sure_one_hot = ez->FALSE; - int sure_many_hot = ez->FALSE; + int sure_one_hot = ez->CONST_FALSE; + int sure_many_hot = ez->CONST_FALSE; - std::vector bits_set = std::vector(undef_y.size(), ez->FALSE); - std::vector bits_clr = std::vector(undef_y.size(), ez->FALSE); + std::vector bits_set = std::vector(undef_y.size(), ez->CONST_FALSE); + std::vector bits_clr = std::vector(undef_y.size(), ez->CONST_FALSE); for (size_t i = 0; i < s.size(); i++) { @@ -463,7 +548,7 @@ struct SatGen if (cell->type == "$pos") { ez->assume(ez->vec_eq(a, yy)); } else { - std::vector zero(a.size(), ez->FALSE); + std::vector zero(a.size(), ez->CONST_FALSE); ez->assume(ez->vec_eq(ez->vec_sub(zero, a), yy)); } @@ -505,7 +590,7 @@ struct SatGen if (cell->type == "$logic_not") ez->SET(ez->NOT(ez->expression(ez->OpOr, a)), yy.at(0)); for (size_t i = 1; i < y.size(); i++) - ez->SET(ez->FALSE, yy.at(i)); + ez->SET(ez->CONST_FALSE, yy.at(i)); if (model_undef) { @@ -527,7 +612,7 @@ struct SatGen log_abort(); for (size_t i = 1; i < undef_y.size(); i++) - ez->SET(ez->FALSE, undef_y.at(i)); + ez->SET(ez->CONST_FALSE, undef_y.at(i)); undefGating(y, yy, undef_y); } @@ -550,7 +635,7 @@ struct SatGen else ez->SET(ez->expression(ez->OpOr, a, b), yy.at(0)); for (size_t i = 1; i < y.size(); i++) - ez->SET(ez->FALSE, yy.at(i)); + ez->SET(ez->CONST_FALSE, yy.at(i)); if (model_undef) { @@ -573,7 +658,7 @@ struct SatGen log_abort(); for (size_t i = 1; i < undef_y.size(); i++) - ez->SET(ez->FALSE, undef_y.at(i)); + ez->SET(ez->CONST_FALSE, undef_y.at(i)); undefGating(y, yy, undef_y); } @@ -611,7 +696,7 @@ struct SatGen if (cell->type == "$gt") ez->SET(is_signed ? ez->vec_gt_signed(a, b) : ez->vec_gt_unsigned(a, b), yy.at(0)); for (size_t i = 1; i < y.size(); i++) - ez->SET(ez->FALSE, yy.at(i)); + ez->SET(ez->CONST_FALSE, yy.at(i)); if (model_undef && (cell->type == "$eqx" || cell->type == "$nex")) { @@ -626,7 +711,7 @@ struct SatGen yy.at(0) = ez->OR(yy.at(0), ez->vec_ne(undef_a, undef_b)); for (size_t i = 0; i < y.size(); i++) - ez->SET(ez->FALSE, undef_y.at(i)); + ez->SET(ez->CONST_FALSE, undef_y.at(i)); ez->assume(ez->vec_eq(y, yy)); } @@ -648,7 +733,7 @@ struct SatGen int undef_y_bit = ez->AND(undef_any, ez->NOT(masked_ne)); for (size_t i = 1; i < undef_y.size(); i++) - ez->SET(ez->FALSE, undef_y.at(i)); + ez->SET(ez->CONST_FALSE, undef_y.at(i)); ez->SET(undef_y_bit, undef_y.at(0)); undefGating(y, yy, undef_y); @@ -670,7 +755,7 @@ struct SatGen std::vector b = importDefSigSpec(cell->getPort("\\B"), timestep); std::vector y = importDefSigSpec(cell->getPort("\\Y"), timestep); - int extend_bit = ez->FALSE; + int extend_bit = ez->CONST_FALSE; if (!cell->type.in("$shift", "$shiftx") && cell->parameters["\\A_SIGNED"].as_bool()) extend_bit = a.back(); @@ -684,16 +769,16 @@ struct SatGen std::vector shifted_a; if (cell->type == "$shl" || cell->type == "$sshl") - shifted_a = ez->vec_shift_left(a, b, false, ez->FALSE, ez->FALSE); + shifted_a = ez->vec_shift_left(a, b, false, ez->CONST_FALSE, ez->CONST_FALSE); if (cell->type == "$shr") - shifted_a = ez->vec_shift_right(a, b, false, ez->FALSE, ez->FALSE); + shifted_a = ez->vec_shift_right(a, b, false, ez->CONST_FALSE, ez->CONST_FALSE); if (cell->type == "$sshr") - shifted_a = ez->vec_shift_right(a, b, false, cell->parameters["\\A_SIGNED"].as_bool() ? a.back() : ez->FALSE, ez->FALSE); + shifted_a = ez->vec_shift_right(a, b, false, cell->parameters["\\A_SIGNED"].as_bool() ? a.back() : ez->CONST_FALSE, ez->CONST_FALSE); if (cell->type == "$shift" || cell->type == "$shiftx") - shifted_a = ez->vec_shift_right(a, b, cell->parameters["\\B_SIGNED"].as_bool(), ez->FALSE, ez->FALSE); + shifted_a = ez->vec_shift_right(a, b, cell->parameters["\\B_SIGNED"].as_bool(), ez->CONST_FALSE, ez->CONST_FALSE); ez->assume(ez->vec_eq(shifted_a, yy)); @@ -704,7 +789,7 @@ struct SatGen std::vector undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep); std::vector undef_a_shifted; - extend_bit = cell->type == "$shiftx" ? ez->TRUE : ez->FALSE; + extend_bit = cell->type == "$shiftx" ? ez->CONST_TRUE : ez->CONST_FALSE; if (!cell->type.in("$shift", "$shiftx") && cell->parameters["\\A_SIGNED"].as_bool()) extend_bit = undef_a.back(); @@ -714,19 +799,19 @@ struct SatGen undef_a.push_back(extend_bit); if (cell->type == "$shl" || cell->type == "$sshl") - undef_a_shifted = ez->vec_shift_left(undef_a, b, false, ez->FALSE, ez->FALSE); + undef_a_shifted = ez->vec_shift_left(undef_a, b, false, ez->CONST_FALSE, ez->CONST_FALSE); if (cell->type == "$shr") - undef_a_shifted = ez->vec_shift_right(undef_a, b, false, ez->FALSE, ez->FALSE); + undef_a_shifted = ez->vec_shift_right(undef_a, b, false, ez->CONST_FALSE, ez->CONST_FALSE); if (cell->type == "$sshr") - undef_a_shifted = ez->vec_shift_right(undef_a, b, false, cell->parameters["\\A_SIGNED"].as_bool() ? undef_a.back() : ez->FALSE, ez->FALSE); + undef_a_shifted = ez->vec_shift_right(undef_a, b, false, cell->parameters["\\A_SIGNED"].as_bool() ? undef_a.back() : ez->CONST_FALSE, ez->CONST_FALSE); if (cell->type == "$shift") - undef_a_shifted = ez->vec_shift_right(undef_a, b, cell->parameters["\\B_SIGNED"].as_bool(), ez->FALSE, ez->FALSE); + undef_a_shifted = ez->vec_shift_right(undef_a, b, cell->parameters["\\B_SIGNED"].as_bool(), ez->CONST_FALSE, ez->CONST_FALSE); if (cell->type == "$shiftx") - undef_a_shifted = ez->vec_shift_right(undef_a, b, cell->parameters["\\B_SIGNED"].as_bool(), ez->TRUE, ez->TRUE); + undef_a_shifted = ez->vec_shift_right(undef_a, b, cell->parameters["\\B_SIGNED"].as_bool(), ez->CONST_TRUE, ez->CONST_TRUE); int undef_any_b = ez->expression(ezSAT::OpOr, undef_b); std::vector undef_all_y_bits(undef_y.size(), undef_any_b); @@ -745,10 +830,10 @@ struct SatGen std::vector yy = model_undef ? ez->vec_var(y.size()) : y; - std::vector tmp(a.size(), ez->FALSE); + std::vector tmp(a.size(), ez->CONST_FALSE); for (int i = 0; i < int(a.size()); i++) { - std::vector shifted_a(a.size(), ez->FALSE); + std::vector shifted_a(a.size(), ez->CONST_FALSE); for (int j = i; j < int(a.size()); j++) shifted_a.at(j) = a.at(j-i); tmp = ez->vec_ite(b.at(i), ez->vec_add(tmp, shifted_a), tmp); @@ -772,25 +857,25 @@ struct SatGen Macc macc; macc.from_cell(cell); - std::vector tmp(SIZE(y), ez->FALSE); + std::vector tmp(GetSize(y), ez->CONST_FALSE); for (auto &port : macc.ports) { std::vector in_a = importDefSigSpec(port.in_a, timestep); std::vector in_b = importDefSigSpec(port.in_b, timestep); - while (SIZE(in_a) < SIZE(y)) - in_a.push_back(port.is_signed && !in_a.empty() ? in_a.back() : ez->FALSE); - in_a.resize(SIZE(y)); + while (GetSize(in_a) < GetSize(y)) + in_a.push_back(port.is_signed && !in_a.empty() ? in_a.back() : ez->CONST_FALSE); + in_a.resize(GetSize(y)); - if (SIZE(in_b)) + if (GetSize(in_b)) { - while (SIZE(in_b) < SIZE(y)) - in_b.push_back(port.is_signed && !in_b.empty() ? in_b.back() : ez->FALSE); - in_b.resize(SIZE(y)); + while (GetSize(in_b) < GetSize(y)) + in_b.push_back(port.is_signed && !in_b.empty() ? in_b.back() : ez->CONST_FALSE); + in_b.resize(GetSize(y)); - for (int i = 0; i < SIZE(in_b); i++) { - std::vector shifted_a(in_a.size(), ez->FALSE); + for (int i = 0; i < GetSize(in_b); i++) { + std::vector shifted_a(in_a.size(), ez->CONST_FALSE); for (int j = i; j < int(in_a.size()); j++) shifted_a.at(j) = in_a.at(j-i); if (port.do_subtract) @@ -808,8 +893,8 @@ struct SatGen } } - for (int i = 0; i < SIZE(b); i++) { - std::vector val(SIZE(y), ez->FALSE); + for (int i = 0; i < GetSize(b); i++) { + std::vector val(GetSize(y), ez->CONST_FALSE); val.at(0) = b.at(i); tmp = ez->vec_add(tmp, val); } @@ -823,7 +908,7 @@ struct SatGen int undef_any_b = ez->expression(ezSAT::OpOr, undef_b); std::vector undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep); - ez->assume(ez->vec_eq(undef_y, std::vector(SIZE(y), ez->OR(undef_any_a, undef_any_b)))); + ez->assume(ez->vec_eq(undef_y, std::vector(GetSize(y), ez->OR(undef_any_a, undef_any_b)))); undefGating(y, tmp, undef_y); } @@ -852,14 +937,14 @@ struct SatGen } std::vector chain_buf = a_u; - std::vector y_u(a_u.size(), ez->FALSE); + std::vector y_u(a_u.size(), ez->CONST_FALSE); for (int i = int(a.size())-1; i >= 0; i--) { - chain_buf.insert(chain_buf.end(), chain_buf.size(), ez->FALSE); + chain_buf.insert(chain_buf.end(), chain_buf.size(), ez->CONST_FALSE); - std::vector b_shl(i, ez->FALSE); + std::vector b_shl(i, ez->CONST_FALSE); b_shl.insert(b_shl.end(), b_u.begin(), b_u.end()); - b_shl.insert(b_shl.end(), chain_buf.size()-b_shl.size(), ez->FALSE); + b_shl.insert(b_shl.end(), chain_buf.size()-b_shl.size(), ez->CONST_FALSE); y_u.at(i) = ez->vec_ge_unsigned(chain_buf, b_shl); chain_buf = ez->vec_ite(y_u.at(i), ez->vec_sub(chain_buf, b_shl), chain_buf); @@ -886,13 +971,13 @@ struct SatGen std::vector div_zero_result; if (cell->type == "$div") { if (cell->parameters["\\A_SIGNED"].as_bool() && cell->parameters["\\B_SIGNED"].as_bool()) { - std::vector all_ones(y.size(), ez->TRUE); - std::vector only_first_one(y.size(), ez->FALSE); - only_first_one.at(0) = ez->TRUE; + std::vector all_ones(y.size(), ez->CONST_TRUE); + std::vector only_first_one(y.size(), ez->CONST_FALSE); + only_first_one.at(0) = ez->CONST_TRUE; div_zero_result = ez->vec_ite(a.back(), only_first_one, all_ones); } else { - div_zero_result.insert(div_zero_result.end(), cell->getPort("\\A").size(), ez->TRUE); - div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), ez->FALSE); + div_zero_result.insert(div_zero_result.end(), cell->getPort("\\A").size(), ez->CONST_TRUE); + 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()); @@ -900,7 +985,7 @@ struct SatGen 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()); else - div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), ez->FALSE); + div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), ez->CONST_FALSE); } ez->assume(ez->vec_eq(yy, ez->vec_ite(ez->expression(ezSAT::OpOr, b), y_tmp, div_zero_result))); } @@ -920,44 +1005,44 @@ struct SatGen std::vector lut; for (auto bit : cell->getParam("\\LUT").bits) - lut.push_back(bit == RTLIL::S1 ? ez->TRUE : ez->FALSE); - while (SIZE(lut) < (1 << SIZE(a))) - lut.push_back(ez->FALSE); - lut.resize(1 << SIZE(a)); + lut.push_back(bit == RTLIL::S1 ? ez->CONST_TRUE : ez->CONST_FALSE); + while (GetSize(lut) < (1 << GetSize(a))) + lut.push_back(ez->CONST_FALSE); + lut.resize(1 << GetSize(a)); if (model_undef) { std::vector undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep); - std::vector t(lut), u(SIZE(t), ez->FALSE); + std::vector t(lut), u(GetSize(t), ez->CONST_FALSE); - for (int i = SIZE(a)-1; i >= 0; i--) + for (int i = GetSize(a)-1; i >= 0; i--) { - std::vector t0(t.begin(), t.begin() + SIZE(t)/2); - std::vector t1(t.begin() + SIZE(t)/2, t.end()); + std::vector t0(t.begin(), t.begin() + GetSize(t)/2); + std::vector t1(t.begin() + GetSize(t)/2, t.end()); - std::vector u0(u.begin(), u.begin() + SIZE(u)/2); - std::vector u1(u.begin() + SIZE(u)/2, u.end()); + std::vector u0(u.begin(), u.begin() + GetSize(u)/2); + std::vector u1(u.begin() + GetSize(u)/2, u.end()); t = ez->vec_ite(a[i], t1, t0); u = ez->vec_ite(undef_a[i], ez->vec_or(ez->vec_xor(t0, t1), ez->vec_or(u0, u1)), ez->vec_ite(a[i], u1, u0)); } - log_assert(SIZE(t) == 1); - log_assert(SIZE(u) == 1); + log_assert(GetSize(t) == 1); + log_assert(GetSize(u) == 1); undefGating(y, t, u); ez->assume(ez->vec_eq(importUndefSigSpec(cell->getPort("\\Y"), timestep), u)); } else { std::vector t = lut; - for (int i = SIZE(a)-1; i >= 0; i--) + for (int i = GetSize(a)-1; i >= 0; i--) { - std::vector t0(t.begin(), t.begin() + SIZE(t)/2); - std::vector t1(t.begin() + SIZE(t)/2, t.end()); + std::vector t0(t.begin(), t.begin() + GetSize(t)/2); + std::vector t1(t.begin() + GetSize(t)/2, t.end()); t = ez->vec_ite(a[i], t1, t0); } - log_assert(SIZE(t) == 1); + log_assert(GetSize(t) == 1); ez->assume(ez->vec_eq(y, t)); } return true; @@ -1008,7 +1093,7 @@ struct SatGen std::vector yy = model_undef ? ez->vec_var(co.size()) : co; - for (int i = 0; i < SIZE(co); i++) + for (int i = 0; i < GetSize(co); i++) ez->SET(yy[i], ez->OR(g[i], ez->AND(p[i], i ? yy[i-1] : ci[0]))); if (model_undef) @@ -1049,12 +1134,12 @@ struct SatGen std::vector def_x = model_undef ? ez->vec_var(x.size()) : x; std::vector def_co = model_undef ? ez->vec_var(co.size()) : co; - log_assert(SIZE(y) == SIZE(x)); - log_assert(SIZE(y) == SIZE(co)); - log_assert(SIZE(ci) == 1); - log_assert(SIZE(bi) == 1); + log_assert(GetSize(y) == GetSize(x)); + log_assert(GetSize(y) == GetSize(co)); + log_assert(GetSize(ci) == 1); + log_assert(GetSize(bi) == 1); - for (int i = 0; i < SIZE(y); i++) + for (int i = 0; i < GetSize(y); i++) { int s1 = a.at(i), s2 = ez->XOR(b.at(i), bi.at(0)), s3 = i ? co.at(i-1) : ci.at(0); ez->SET(def_x.at(i), ez->XOR(s1, s2)); @@ -1084,7 +1169,7 @@ struct SatGen all_inputs_undef.insert(all_inputs_undef.end(), undef_bi.begin(), undef_bi.end()); int undef_any = ez->expression(ezSAT::OpOr, all_inputs_undef); - for (int i = 0; i < SIZE(undef_y); i++) { + for (int i = 0; i < GetSize(undef_y); i++) { ez->SET(undef_y.at(i), undef_any); ez->SET(undef_x.at(i), ez->OR(undef_a.at(i), undef_b.at(i), undef_bi.at(0))); ez->SET(undef_co.at(i), undef_any); @@ -1144,6 +1229,25 @@ struct SatGen return true; } + if (cell->type == "$_BUF_" || cell->type == "$equiv") + { + std::vector a = importDefSigSpec(cell->getPort("\\A"), timestep); + std::vector y = importDefSigSpec(cell->getPort("\\Y"), timestep); + extendSignalWidthUnary(a, y, cell); + + std::vector yy = model_undef ? ez->vec_var(y.size()) : y; + ez->assume(ez->vec_eq(a, yy)); + + if (model_undef) { + std::vector undef_a = importUndefSigSpec(cell->getPort("\\A"), timestep); + std::vector undef_y = importUndefSigSpec(cell->getPort("\\Y"), timestep); + extendSignalWidthUnary(undef_a, undef_y, cell, false); + ez->assume(ez->vec_eq(undef_a, undef_y)); + undefGating(y, yy, undef_y); + } + return true; + } + if (cell->type == "$assert") { std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep)); @@ -1152,10 +1256,20 @@ 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; } }; +YOSYS_NAMESPACE_END + #endif diff --git a/kernel/sigtools.h b/kernel/sigtools.h index 32ef444a..f92a87db 100644 --- a/kernel/sigtools.h +++ b/kernel/sigtools.h @@ -29,9 +29,10 @@ struct SigPool struct bitDef_t : public std::pair { bitDef_t() : std::pair(NULL, 0) { } bitDef_t(const RTLIL::SigBit &bit) : std::pair(bit.wire, bit.offset) { } + unsigned int hash() const { return first->name.hash() + second; } }; - std::set bits; + pool bits; void clear() { @@ -66,8 +67,8 @@ struct SigPool void expand(RTLIL::SigSpec from, RTLIL::SigSpec to) { - log_assert(SIZE(from) == SIZE(to)); - for (int i = 0; i < SIZE(from); i++) { + log_assert(GetSize(from) == GetSize(to)); + for (int i = 0; i < GetSize(from); i++) { bitDef_t bit_from(from[i]), bit_to(to[i]); if (bit_from.first != NULL && bit_to.first != NULL && bits.count(bit_from) > 0) bits.insert(bit_to); @@ -122,13 +123,13 @@ struct SigPool RTLIL::SigSpec export_all() { - std::set sig; + pool sig; for (auto &bit : bits) sig.insert(RTLIL::SigBit(bit.first, bit.second)); return sig; } - size_t size() + size_t size() const { return bits.size(); } @@ -140,9 +141,10 @@ struct SigSet struct bitDef_t : public std::pair { bitDef_t() : std::pair(NULL, 0) { } bitDef_t(const RTLIL::SigBit &bit) : std::pair(bit.wire, bit.offset) { } + unsigned int hash() const { return first->name.hash() + second; } }; - std::map> bits; + dict> bits; void clear() { @@ -193,6 +195,15 @@ struct SigSet } } + void find(RTLIL::SigSpec sig, pool &result) + { + for (auto &bit : sig) + if (bit.wire != NULL) { + auto &data = bits[bit]; + result.insert(data.begin(), data.end()); + } + } + std::set find(RTLIL::SigSpec sig) { std::set result; @@ -214,6 +225,7 @@ struct SigMap struct bitDef_t : public std::pair { bitDef_t() : std::pair(NULL, 0) { } bitDef_t(const RTLIL::SigBit &bit) : std::pair(bit.wire, bit.offset) { } + unsigned int hash() const { return first->name.hash() + second; } }; struct shared_bit_data_t { @@ -221,7 +233,7 @@ struct SigMap std::set bits; }; - std::map bits; + dict bits; SigMap(RTLIL::Module *module = NULL) { @@ -346,9 +358,9 @@ struct SigMap void add(RTLIL::SigSpec from, RTLIL::SigSpec to) { - log_assert(SIZE(from) == SIZE(to)); + log_assert(GetSize(from) == GetSize(to)); - for (int i = 0; i < SIZE(from); i++) + for (int i = 0; i < GetSize(from); i++) { RTLIL::SigBit &bf = from[i]; RTLIL::SigBit &bt = to[i]; diff --git a/kernel/utils.h b/kernel/utils.h index a03caf80..2ec6182e 100644 --- a/kernel/utils.h +++ b/kernel/utils.h @@ -25,21 +25,23 @@ #ifndef UTILS_H #define UTILS_H +YOSYS_NAMESPACE_BEGIN + // ------------------------------------------------ // A map-like container, but you can save and restore the state // ------------------------------------------------ -template> +template> struct stackmap { private: - std::vector> backup_state; - std::map current_state; + std::vector> backup_state; + dict current_state; static T empty_tuple; public: stackmap() { } - stackmap(const std::map &other) : current_state(other) { } + stackmap(const dict &other) : current_state(other) { } template void operator=(const Other &other) @@ -81,7 +83,7 @@ public: void reset(const Key &k) { - for (int i = SIZE(backup_state)-1; i >= 0; i--) + for (int i = GetSize(backup_state)-1; i >= 0; i--) if (backup_state[i].count(k) != 0) { if (backup_state[i].at(k) == nullptr) current_state.erase(k); @@ -92,7 +94,7 @@ public: current_state.erase(k); } - const std::map &stdmap() + const dict &stdmap() { return current_state; } @@ -126,12 +128,12 @@ public: // A simple class for topological sorting // ------------------------------------------------ -template +template> struct TopoSort { bool analyze_loops, found_loops; - std::map> database; - std::set> loops; + std::map, C> database; + std::set> loops; std::vector sorted; TopoSort() @@ -143,7 +145,7 @@ struct TopoSort void node(T n) { if (database.count(n) == 0) - database[n] = std::set(); + database[n] = std::set(); } void edge(T left, T right) @@ -152,13 +154,13 @@ struct TopoSort database[right].insert(left); } - void sort_worker(const T &n, std::set &marked_cells, std::set &active_cells, std::vector &active_stack) + void sort_worker(const T &n, std::set &marked_cells, std::set &active_cells, std::vector &active_stack) { if (active_cells.count(n)) { found_loops = true; if (analyze_loops) { - std::set loop; - for (int i = SIZE(active_stack)-1; i >= 0; i--) { + std::set loop; + for (int i = GetSize(active_stack)-1; i >= 0; i--) { loop.insert(active_stack[i]); if (active_stack[i] == n) break; @@ -195,16 +197,18 @@ struct TopoSort sorted.clear(); found_loops = false; - std::set marked_cells; - std::set active_cells; + std::set marked_cells; + std::set active_cells; std::vector active_stack; for (auto &it : database) sort_worker(it.first, marked_cells, active_cells, active_stack); - log_assert(SIZE(sorted) == SIZE(database)); + log_assert(GetSize(sorted) == GetSize(database)); return !found_loops; } }; +YOSYS_NAMESPACE_END + #endif diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 0ecb4cda..884b2c59 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -18,26 +18,112 @@ */ #include "kernel/yosys.h" +#include "kernel/celltypes.h" #ifdef YOSYS_ENABLE_READLINE # include # include #endif -#include -#include +#ifdef YOSYS_ENABLE_PLUGINS +# include +#endif + +#ifdef _WIN32 +# include +# include +#elif defined(__APPLE__) +# include +# include +# include +# include +#else +# include +# include +# include +# include +#endif + #include #include YOSYS_NAMESPACE_BEGIN int autoidx = 1; +int yosys_xtrace = 0; RTLIL::Design *yosys_design = NULL; +CellTypes yosys_celltypes; #ifdef YOSYS_ENABLE_TCL Tcl_Interp *yosys_tcl_interp = NULL; #endif +bool memhasher_active = false; +uint32_t memhasher_rng = 123456; +std::vector memhasher_store; + +void memhasher_on() +{ +#ifdef __linux__ + memhasher_rng += time(NULL) << 16 ^ getpid(); +#endif + memhasher_store.resize(0x10000); + memhasher_active = true; +} + +void memhasher_off() +{ + for (auto p : memhasher_store) + if (p) free(p); + memhasher_store.clear(); + memhasher_active = false; +} + +void memhasher_do() +{ + memhasher_rng ^= memhasher_rng << 13; + memhasher_rng ^= memhasher_rng >> 17; + memhasher_rng ^= memhasher_rng << 5; + + int size, index = (memhasher_rng >> 4) & 0xffff; + switch (memhasher_rng & 7) { + case 0: size = 16; break; + case 1: size = 256; break; + case 2: size = 1024; break; + case 3: size = 4096; break; + default: size = 0; + } + if (index < 16) size *= 16; + memhasher_store[index] = realloc(memhasher_store[index], size); +} + +void yosys_banner() +{ + log("\n"); + log(" /----------------------------------------------------------------------------\\\n"); + log(" | |\n"); + log(" | yosys -- Yosys Open SYnthesis Suite |\n"); + log(" | |\n"); + log(" | Copyright (C) 2012 - 2015 Clifford Wolf |\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"); + log(" | copyright notice and this permission notice appear in all copies. |\n"); + log(" | |\n"); + log(" | THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |\n"); + log(" | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |\n"); + log(" | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |\n"); + log(" | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |\n"); + log(" | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |\n"); + log(" | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |\n"); + log(" | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |\n"); + log(" | |\n"); + log(" \\----------------------------------------------------------------------------/\n"); + log("\n"); + log(" %s\n", yosys_version_str); + log("\n"); +} + std::string stringf(const char *fmt, ...) { std::string string; @@ -55,8 +141,22 @@ std::string vstringf(const char *fmt, va_list ap) std::string string; char *str = NULL; +#ifdef _WIN32 + int sz = 64, rc; + while (1) { + va_list apc; + va_copy(apc, ap); + str = (char*)realloc(str, sz); + rc = vsnprintf(str, sz, fmt, apc); + va_end(apc); + if (rc >= 0 && rc < sz) + break; + sz *= 2; + } +#else if (vasprintf(&str, fmt, ap) < 0) str = NULL; +#endif if (str != NULL) { string = str; @@ -66,15 +166,264 @@ std::string vstringf(const char *fmt, va_list ap) return string; } -int SIZE(RTLIL::Wire *wire) +int readsome(std::istream &f, char *s, int n) +{ + int rc = f.readsome(s, n); + + // f.readsome() sometimes returns 0 on a non-empty stream.. + if (rc == 0) { + int c = f.get(); + if (c != EOF) { + *s = c; + rc = 1; + } + } + + return rc; +} + +std::string next_token(std::string &text, const char *sep) +{ + size_t pos_begin = text.find_first_not_of(sep); + + if (pos_begin == std::string::npos) + pos_begin = text.size(); + + size_t pos_end = text.find_first_of(sep, pos_begin); + + if (pos_end == std::string::npos) + pos_end = text.size(); + + std::string token = text.substr(pos_begin, pos_end-pos_begin); + text = text.substr(pos_end); + return token; +} + +// this is very similar to fnmatch(). the exact rules used by this +// function are: +// +// ? matches any character except +// * matches any sequence of characters +// [...] matches any of the characters in the list +// [!..] matches any of the characters not in the list +// +// a backslash may be used to escape the next characters in the +// pattern. each special character can also simply match itself. +// +bool patmatch(const char *pattern, const char *string) +{ + if (*pattern == 0) + return *string == 0; + + if (*pattern == '\\') { + if (pattern[1] == string[0] && patmatch(pattern+2, string+1)) + return true; + } + + if (*pattern == '?') { + if (*string == 0) + return false; + return patmatch(pattern+1, string+1); + } + + if (*pattern == '*') { + while (*string) { + if (patmatch(pattern+1, string++)) + return true; + } + return pattern[1] == 0; + } + + if (*pattern == '[') { + bool found_match = false; + bool inverted_list = pattern[1] == '!'; + const char *p = pattern + (inverted_list ? 1 : 0); + + while (*++p) { + if (*p == ']') { + if (found_match != inverted_list && patmatch(p+1, string+1)) + return true; + break; + } + + if (*p == '\\') { + if (*++p == *string) + found_match = true; + } else + if (*p == *string) + found_match = true; + } + } + + if (*pattern == *string) + return patmatch(pattern+1, string+1); + + return false; +} + +int run_command(const std::string &command, std::function process_line) +{ + if (!process_line) + return system(command.c_str()); + + FILE *f = popen(command.c_str(), "r"); + if (f == nullptr) + return -1; + + std::string line; + char logbuf[128]; + while (fgets(logbuf, 128, f) != NULL) { + line += logbuf; + if (!line.empty() && line.back() == '\n') + process_line(line), line.clear(); + } + if (!line.empty()) + process_line(line); + + int ret = pclose(f); + if (ret < 0) + return -1; +#ifdef _WIN32 + return ret; +#else + return WEXITSTATUS(ret); +#endif +} + +std::string make_temp_file(std::string template_str) +{ +#ifdef _WIN32 + if (template_str.rfind("/tmp/", 0) == 0) { +# ifdef __MINGW32__ + char longpath[MAX_PATH + 1]; + char shortpath[MAX_PATH + 1]; +# else + WCHAR longpath[MAX_PATH + 1]; + TCHAR shortpath[MAX_PATH + 1]; +# endif + if (!GetTempPath(MAX_PATH+1, longpath)) + log_error("GetTempPath() failed.\n"); + if (!GetShortPathName(longpath, shortpath, MAX_PATH + 1)) + log_error("GetShortPathName() failed.\n"); + std::string path; + for (int i = 0; shortpath[i]; i++) + path += char(shortpath[i]); + template_str = stringf("%s\\%s", path.c_str(), template_str.c_str() + 5); + } + + size_t pos = template_str.rfind("XXXXXX"); + log_assert(pos != std::string::npos); + + while (1) { + for (int i = 0; i < 6; i++) { + static std::string y = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + static uint32_t x = 314159265 ^ uint32_t(time(NULL)); + x ^= x << 13, x ^= x >> 17, x ^= x << 5; + template_str[pos+i] = y[x % y.size()]; + } + if (_access(template_str.c_str(), 0) != 0) + break; + } +#else + size_t pos = template_str.rfind("XXXXXX"); + log_assert(pos != std::string::npos); + + int suffixlen = GetSize(template_str) - pos - 6; + + char *p = strdup(template_str.c_str()); + close(mkstemps(p, suffixlen)); + template_str = p; + free(p); +#endif + + return template_str; +} + +std::string make_temp_dir(std::string template_str) +{ +#ifdef _WIN32 + template_str = make_temp_file(template_str); + mkdir(template_str.c_str()); + return template_str; +#else +# ifndef NDEBUG + size_t pos = template_str.rfind("XXXXXX"); + log_assert(pos != std::string::npos); + + int suffixlen = GetSize(template_str) - pos - 6; + log_assert(suffixlen == 0); +# endif + + char *p = strdup(template_str.c_str()); + p = mkdtemp(p); + log_assert(p != NULL); + template_str = p; + free(p); + + return template_str; +#endif +} + +#ifdef _WIN32 +bool check_file_exists(std::string filename, bool) +{ + return _access(filename.c_str(), 0) == 0; +} +#else +bool check_file_exists(std::string filename, bool is_exec) +{ + return access(filename.c_str(), is_exec ? X_OK : F_OK) == 0; +} +#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 + run_command(stringf("rmdir /s /q \"%s\"", dirname.c_str())); +#else + struct stat stbuf; + struct dirent **namelist; + int n = scandir(dirname.c_str(), &namelist, nullptr, alphasort); + log_assert(n >= 0); + for (int i = 0; i < n; i++) { + if (strcmp(namelist[i]->d_name, ".") && strcmp(namelist[i]->d_name, "..")) { + std::string buffer = stringf("%s/%s", dirname.c_str(), namelist[i]->d_name); + if (!stat(buffer.c_str(), &stbuf) && S_ISREG(stbuf.st_mode)) { + remove(buffer.c_str()); + } else + remove_directory(buffer); + } + free(namelist[i]); + } + free(namelist); + rmdir(dirname.c_str()); +#endif +} + +int GetSize(RTLIL::Wire *wire) { return wire->width; } void yosys_setup() { + // if there are already IdString objects then we have a global initialization order bug + IdString empty_id; + log_assert(empty_id.index_ == 0); + IdString::get_reference(empty_id.index_); + Pass::init_register(); yosys_design = new RTLIL::Design; + yosys_celltypes.setup(); log_push(); } @@ -92,6 +441,7 @@ void yosys_shutdown() log_files.clear(); Pass::done_register(); + yosys_celltypes.clear(); #ifdef YOSYS_ENABLE_TCL if (yosys_tcl_interp != NULL) { @@ -101,20 +451,33 @@ void yosys_shutdown() } #endif +#ifdef YOSYS_ENABLE_PLUGINS for (auto &it : loaded_plugins) dlclose(it.second); loaded_plugins.clear(); loaded_plugin_aliases.clear(); +#endif + + IdString empty_id; + IdString::put_reference(empty_id.index_); } RTLIL::IdString new_id(std::string file, int line, std::string func) { - std::string str = "$auto$"; +#ifdef _WIN32 + size_t pos = file.find_last_of("/\\"); +#else size_t pos = file.find_last_of('/'); - str += pos != std::string::npos ? file.substr(pos+1) : file; - str += stringf(":%d:%s$%d", line, func.c_str(), autoidx++); - return str; +#endif + if (pos != std::string::npos) + file = file.substr(pos+1); + + pos = func.find_last_of(':'); + if (pos != std::string::npos) + func = func.substr(pos+1); + + return stringf("$auto$%s:%d:%s$%d", file.c_str(), line, func.c_str(), autoidx++); } RTLIL::Design *yosys_get_design() @@ -210,9 +573,9 @@ struct TclPass : public Pass { #endif #if defined(__linux__) -std::string proc_self_dirname () +std::string proc_self_dirname() { - char path [PATH_MAX]; + char path[PATH_MAX]; ssize_t buflen = readlink("/proc/self/exe", path, sizeof(path)); if (buflen < 0) { log_error("readlink(\"/proc/self/exe\") failed: %s\n", strerror(errno)); @@ -222,10 +585,9 @@ std::string proc_self_dirname () return std::string(path, buflen); } #elif defined(__APPLE__) -#include -std::string proc_self_dirname () +std::string proc_self_dirname() { - char * path = NULL; + char *path = NULL; uint32_t buflen = 0; while (_NSGetExecutablePath(path, &buflen) != 0) path = (char *) realloc((void *) path, buflen); @@ -233,8 +595,32 @@ std::string proc_self_dirname () buflen--; return std::string(path, buflen); } +#elif defined(_WIN32) +std::string proc_self_dirname() +{ + int i = 0; +# ifdef __MINGW32__ + char longpath[MAX_PATH + 1]; + char shortpath[MAX_PATH + 1]; +# else + WCHAR longpath[MAX_PATH + 1]; + TCHAR shortpath[MAX_PATH + 1]; +# endif + if (!GetModuleFileName(0, longpath, MAX_PATH+1)) + log_error("GetModuleFileName() failed.\n"); + if (!GetShortPathName(longpath, shortpath, MAX_PATH+1)) + log_error("GetShortPathName() failed.\n"); + while (shortpath[i] != 0) + i++; + while (i > 0 && shortpath[i-1] != '/' && shortpath[i-1] != '\\') + shortpath[--i] = 0; + std::string path; + for (i = 0; shortpath[i]; i++) + path += char(shortpath[i]); + return path; +} #elif defined(EMSCRIPTEN) -std::string proc_self_dirname () +std::string proc_self_dirname() { return "/"; } @@ -242,17 +628,33 @@ std::string proc_self_dirname () #error Dont know how to determine process executable base path! #endif -std::string proc_share_dirname () +#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 + 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 std::string proc_share_path = proc_self_path + "share/"; - if (access(proc_share_path.c_str(), X_OK) == 0) + if (check_file_exists(proc_share_path, true)) return proc_share_path; proc_share_path = proc_self_path + "../share/yosys/"; - if (access(proc_share_path.c_str(), X_OK) == 0) + if (check_file_exists(proc_share_path, true)) return proc_share_path; +# endif log_error("proc_share_dirname: unable to determine share/ directory!\n"); } +#endif bool fgetline(FILE *f, std::string &buffer) { @@ -275,15 +677,15 @@ static void handle_label(std::string &command, bool &from_to_active, const std:: int pos = 0; std::string label; - while (pos < SIZE(command) && (command[pos] == ' ' || command[pos] == '\t')) + while (pos < GetSize(command) && (command[pos] == ' ' || command[pos] == '\t')) pos++; - while (pos < SIZE(command) && command[pos] != ' ' && command[pos] != '\t' && command[pos] != '\r' && command[pos] != '\n') + while (pos < GetSize(command) && command[pos] != ' ' && command[pos] != '\t' && command[pos] != '\r' && command[pos] != '\n') label += command[pos++]; - if (label.back() == ':' && SIZE(label) > 1) + if (label.back() == ':' && GetSize(label) > 1) { - label = label.substr(0, SIZE(label)-1); + label = label.substr(0, GetSize(label)-1); command = command.substr(pos); if (label == run_from) @@ -293,8 +695,11 @@ static void handle_label(std::string &command, bool &from_to_active, const std:: } } -void run_frontend(std::string filename, std::string command, RTLIL::Design *design, std::string *backend_command, std::string *from_to_label) +void run_frontend(std::string filename, std::string command, std::string *backend_command, std::string *from_to_label, RTLIL::Design *design) { + if (design == nullptr) + design = yosys_design; + if (command == "auto") { if (filename.size() > 2 && filename.substr(filename.size()-2) == ".v") command = "verilog"; @@ -361,9 +766,9 @@ void run_frontend(std::string filename, std::string command, RTLIL::Design *desi Pass::call(design, command); } } - catch (log_cmd_error_expection) { + catch (...) { Frontend::current_script_file = backup_script_file; - throw log_cmd_error_expection(); + throw; } Frontend::current_script_file = backup_script_file; @@ -386,15 +791,26 @@ void run_frontend(std::string filename, std::string command, RTLIL::Design *desi Frontend::frontend_call(design, NULL, filename, command); } +void run_frontend(std::string filename, std::string command, RTLIL::Design *design) +{ + run_frontend(filename, command, nullptr, nullptr, design); +} + void run_pass(std::string command, RTLIL::Design *design) { - log("\n-- Running pass `%s' --\n", command.c_str()); + if (design == nullptr) + design = yosys_design; + + log("\n-- Running command `%s' --\n", command.c_str()); Pass::call(design, command); } void run_backend(std::string filename, std::string command, RTLIL::Design *design) { + if (design == nullptr) + design = yosys_design; + if (command == "auto") { if (filename.size() > 2 && filename.substr(filename.size()-2) == ".v") command = "verilog"; @@ -518,11 +934,16 @@ void shell(RTLIL::Design *design) char *command = NULL; #ifdef YOSYS_ENABLE_READLINE while ((command = readline(create_prompt(design, recursion_counter))) != NULL) + { #else char command_buffer[4096]; - while ((command = fgets(command_buffer, 4096, stdin)) != NULL) -#endif + while (1) { + fputs(create_prompt(design, recursion_counter), stdout); + fflush(stdout); + if ((command = fgets(command_buffer, 4096, stdin)) == NULL) + break; +#endif if (command[strspn(command, " \t\r\n")] == 0) continue; #ifdef YOSYS_ENABLE_READLINE @@ -540,7 +961,7 @@ void shell(RTLIL::Design *design) try { log_assert(design->selection_stack.size() == 1); Pass::call(design, command); - } catch (log_cmd_error_expection) { + } catch (log_cmd_error_exception) { while (design->selection_stack.size() > 1) design->selection_stack.pop_back(); log_reset_stack(); @@ -634,9 +1055,9 @@ struct ScriptPass : public Pass { if (args.size() < 2) log_cmd_error("Missing script file.\n"); else if (args.size() == 2) - run_frontend(args[1], "script", design, NULL, NULL); + run_frontend(args[1], "script", design); else if (args.size() == 3) - run_frontend(args[1], "script", design, NULL, &args[2]); + run_frontend(args[1], "script", NULL, &args[2], design); else extra_args(args, 2, design, false); } diff --git a/kernel/yosys.h b/kernel/yosys.h index b571e177..231dd4de 100644 --- a/kernel/yosys.h +++ b/kernel/yosys.h @@ -45,7 +45,11 @@ #include #include #include +#include +#include #include +#include +#include #include #include @@ -56,44 +60,164 @@ #include #include #include +#include #include +#ifndef _YOSYS_ +# error It looks like you are trying to build Yosys without the config defines set. \ + When building Yosys with a custom make system, make sure you set all the \ + defines the Yosys Makefile would set for your build configuration. +#endif + +#ifdef YOSYS_ENABLE_TCL +# include +#endif + +#ifdef _WIN32 +# undef NOMINMAX +# define NOMINMAX 1 +# undef YY_NO_UNISTD_H +# define YY_NO_UNISTD_H 1 + +# include +# include +# include + +# define strtok_r strtok_s +# define strdup _strdup +# define snprintf _snprintf +# define getcwd _getcwd +# define mkdir _mkdir +# define popen _popen +# define pclose _pclose +# define PATH_MAX MAX_PATH + +# ifndef __MINGW32__ +# define isatty _isatty +# define fileno _fileno +# endif +#endif + #define PRIVATE_NAMESPACE_BEGIN namespace { #define PRIVATE_NAMESPACE_END } - -#if 0 -# define YOSYS_NAMESPACE_BEGIN namespace Yosys { -# define YOSYS_NAMESPACE_END } -# define YOSYS_NAMESPACE_PREFIX Yosys:: -# define USING_YOSYS_NAMESPACE using namespace Yosys; -#else -# define YOSYS_NAMESPACE_BEGIN -# define YOSYS_NAMESPACE_END -# define YOSYS_NAMESPACE_PREFIX -# define USING_YOSYS_NAMESPACE -#endif +#define YOSYS_NAMESPACE_BEGIN namespace Yosys { +#define YOSYS_NAMESPACE_END } +#define YOSYS_NAMESPACE_PREFIX Yosys:: +#define USING_YOSYS_NAMESPACE using namespace Yosys; #if __cplusplus >= 201103L -# define OVERRIDE override -# define FINAL final +# define YS_OVERRIDE override +# define YS_FINAL final #else -# define OVERRIDE -# define FINAL +# define YS_OVERRIDE +# define YS_FINAL +#endif + +#if defined(__GNUC__) || defined(__clang__) +# define YS_ATTRIBUTE(...) __attribute__((__VA_ARGS__)) +# define YS_NORETURN +#elif defined(_MSC_VER) +# define YS_ATTRIBUTE(...) +# define YS_NORETURN __declspec(noreturn) +#else +# define YS_ATTRIBUTE(...) +# define YS_NORETURN #endif YOSYS_NAMESPACE_BEGIN +// Note: All headers included in hashlib.h must be included +// outside of YOSYS_NAMESPACE before this or bad things will happen. +#ifdef HASHLIB_H +# undef HASHLIB_H +# include "kernel/hashlib.h" +#else +# include "kernel/hashlib.h" +# undef HASHLIB_H +#endif + +using std::vector; +using std::string; +using std::pair; + +using hashlib::mkhash; +using hashlib::mkhash_init; +using hashlib::mkhash_add; +using hashlib::mkhash_xorshift; +using hashlib::hash_ops; +using hashlib::hash_cstr_ops; +using hashlib::hash_ptr_ops; +using hashlib::hash_obj_ops; +using hashlib::dict; +using hashlib::idict; +using hashlib::pool; + namespace RTLIL { struct IdString; + struct Const; + struct SigBit; struct SigSpec; struct Wire; struct Cell; + struct Module; + struct Design; + struct Monitor; } -std::string stringf(const char *fmt, ...); +namespace AST { + struct AstNode; +} + +using RTLIL::IdString; +using RTLIL::Const; +using RTLIL::SigBit; +using RTLIL::SigSpec; +using RTLIL::Wire; +using RTLIL::Cell; +using RTLIL::Module; +using RTLIL::Design; + +namespace hashlib { + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; +} + +void memhasher_on(); +void memhasher_off(); +void memhasher_do(); + +extern bool memhasher_active; +inline void memhasher() { if (memhasher_active) memhasher_do(); } + +void yosys_banner(); +std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2)); std::string vstringf(const char *fmt, va_list ap); -template int SIZE(const T &obj) { return obj.size(); } -int SIZE(RTLIL::Wire *wire); +int readsome(std::istream &f, char *s, int n); +std::string next_token(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 process_line = std::function()); +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 int GetSize(const T &obj) { return obj.size(); } +int GetSize(RTLIL::Wire *wire); + +extern int autoidx; +extern int yosys_xtrace; YOSYS_NAMESPACE_END @@ -103,15 +227,19 @@ YOSYS_NAMESPACE_END YOSYS_NAMESPACE_BEGIN +using RTLIL::State; + +namespace hashlib { + template<> struct hash_ops : hash_ops {}; +} + void yosys_setup(); void yosys_shutdown(); #ifdef YOSYS_ENABLE_TCL -#include Tcl_Interp *yosys_get_tcl_interp(); #endif -extern int autoidx; extern RTLIL::Design *yosys_design; RTLIL::IdString new_id(std::string file, int line, std::string func); @@ -127,9 +255,10 @@ std::string proc_self_dirname(); std::string proc_share_dirname(); const char *create_prompt(RTLIL::Design *design, int recursion_counter); -void run_frontend(std::string filename, std::string command, RTLIL::Design *design, std::string *backend_command, std::string *from_to_label); -void run_pass(std::string command, RTLIL::Design *design); -void run_backend(std::string filename, std::string command, RTLIL::Design *design); +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); +void run_frontend(std::string filename, std::string command, RTLIL::Design *design = nullptr); +void run_backend(std::string filename, std::string command, RTLIL::Design *design = nullptr); void shell(RTLIL::Design *design); // from kernel/version_*.o (cc source generated from Makefile) diff --git a/libs/ezsat/ezminisat.cc b/libs/ezsat/ezminisat.cc index dc4e5d28..dee82a8d 100644 --- a/libs/ezsat/ezminisat.cc +++ b/libs/ezsat/ezminisat.cc @@ -27,7 +27,10 @@ #include #include #include -#include + +#ifndef _WIN32 +# include +#endif #include "../minisat/Solver.h" #include "../minisat/SimpSolver.h" @@ -37,8 +40,8 @@ ezMiniSAT::ezMiniSAT() : minisatSolver(NULL) minisatSolver = NULL; foundContradiction = false; - freeze(TRUE); - freeze(FALSE); + freeze(CONST_TRUE); + freeze(CONST_FALSE); } ezMiniSAT::~ezMiniSAT() @@ -77,6 +80,7 @@ bool ezMiniSAT::eliminated(int idx) } #endif +#ifndef _WIN32 ezMiniSAT *ezMiniSAT::alarmHandlerThis = NULL; clock_t ezMiniSAT::alarmHandlerTimeout = 0; @@ -88,6 +92,7 @@ void ezMiniSAT::alarmHandler(int) } else alarm(1); } +#endif bool ezMiniSAT::solver(const std::vector &modelExpressions, std::vector &modelValues, const std::vector &assumptions) { @@ -174,6 +179,7 @@ contradiction: #endif } +#ifndef _WIN32 struct sigaction sig_action; struct sigaction old_sig_action; int old_alarm_timeout = 0; @@ -188,9 +194,11 @@ contradiction: sigaction(SIGALRM, &sig_action, &old_sig_action); alarm(1); } +#endif bool foundSolution = minisatSolver->solve(assumps); +#ifndef _WIN32 if (solverTimeout > 0) { if (alarmHandlerTimeout == 0) solverTimoutStatus = true; @@ -198,6 +206,7 @@ contradiction: sigaction(SIGALRM, &old_sig_action, NULL); alarm(old_alarm_timeout); } +#endif if (!foundSolution) { #if !EZMINISAT_INCREMENTAL diff --git a/libs/ezsat/ezminisat.h b/libs/ezsat/ezminisat.h index ac9c071c..5b5252d8 100644 --- a/libs/ezsat/ezminisat.h +++ b/libs/ezsat/ezminisat.h @@ -51,9 +51,11 @@ private: std::set cnfFrozenVars; #endif +#ifndef _WIN32 static ezMiniSAT *alarmHandlerThis; static clock_t alarmHandlerTimeout; static void alarmHandler(int); +#endif public: ezMiniSAT(); diff --git a/libs/ezsat/ezsat.cc b/libs/ezsat/ezsat.cc index 13ed112e..8d232f33 100644 --- a/libs/ezsat/ezsat.cc +++ b/libs/ezsat/ezsat.cc @@ -22,14 +22,28 @@ #include #include #include +#include #include -const int ezSAT::TRUE = 1; -const int ezSAT::FALSE = 2; +const int ezSAT::CONST_TRUE = 1; +const int ezSAT::CONST_FALSE = 2; + +static std::string my_int_to_string(int i) +{ +#ifdef __MINGW32__ + char buffer[64]; + snprintf(buffer, 64, "%d", i); + return buffer; +#else + return std::to_string(i); +#endif +} ezSAT::ezSAT() { + statehash = 5381; + flag_keep_cnf = false; flag_non_incremental = false; @@ -42,20 +56,25 @@ ezSAT::ezSAT() solverTimeout = 0; solverTimoutStatus = false; - literal("TRUE"); - literal("FALSE"); + literal("CONST_TRUE"); + literal("CONST_FALSE"); - assert(literal("TRUE") == TRUE); - assert(literal("FALSE") == FALSE); + assert(literal("CONST_TRUE") == CONST_TRUE); + assert(literal("CONST_FALSE") == CONST_FALSE); } ezSAT::~ezSAT() { } +void ezSAT::addhash(unsigned int h) +{ + statehash = ((statehash << 5) + statehash) ^ h; +} + int ezSAT::value(bool val) { - return val ? TRUE : FALSE; + return val ? CONST_TRUE : CONST_FALSE; } int ezSAT::literal() @@ -101,15 +120,21 @@ int ezSAT::expression(OpId op, const std::vector &args) myArgs.reserve(args.size()); bool xorRemovedOddTrues = false; + addhash(__LINE__); + addhash(op); + for (auto arg : args) { + addhash(__LINE__); + addhash(arg); + if (arg == 0) continue; - if (op == OpAnd && arg == TRUE) + if (op == OpAnd && arg == CONST_TRUE) continue; - if ((op == OpOr || op == OpXor) && arg == FALSE) + if ((op == OpOr || op == OpXor) && arg == CONST_FALSE) continue; - if (op == OpXor && arg == TRUE) { + if (op == OpXor && arg == CONST_TRUE) { xorRemovedOddTrues = !xorRemovedOddTrues; continue; } @@ -131,29 +156,29 @@ int ezSAT::expression(OpId op, const std::vector &args) { case OpNot: assert(myArgs.size() == 1); - if (myArgs[0] == TRUE) - return FALSE; - if (myArgs[0] == FALSE) - return TRUE; + if (myArgs[0] == CONST_TRUE) + return CONST_FALSE; + if (myArgs[0] == CONST_FALSE) + return CONST_TRUE; break; case OpAnd: if (myArgs.size() == 0) - return TRUE; + return CONST_TRUE; if (myArgs.size() == 1) return myArgs[0]; break; case OpOr: if (myArgs.size() == 0) - return FALSE; + return CONST_FALSE; if (myArgs.size() == 1) return myArgs[0]; break; case OpXor: if (myArgs.size() == 0) - return xorRemovedOddTrues ? TRUE : FALSE; + return xorRemovedOddTrues ? CONST_TRUE : CONST_FALSE; if (myArgs.size() == 1) return xorRemovedOddTrues ? NOT(myArgs[0]) : myArgs[0]; break; @@ -161,15 +186,15 @@ int ezSAT::expression(OpId op, const std::vector &args) case OpIFF: assert(myArgs.size() >= 1); if (myArgs.size() == 1) - return TRUE; + return CONST_TRUE; // FIXME: Add proper const folding break; case OpITE: assert(myArgs.size() == 3); - if (myArgs[0] == TRUE) + if (myArgs[0] == CONST_TRUE) return myArgs[1]; - if (myArgs[0] == FALSE) + if (myArgs[0] == CONST_FALSE) return myArgs[2]; break; @@ -183,12 +208,18 @@ int ezSAT::expression(OpId op, const std::vector &args) if (expressionsCache.count(myExpr) > 0) { id = expressionsCache.at(myExpr); } else { - id = -(expressions.size() + 1); + id = -(int(expressions.size()) + 1); expressionsCache[myExpr] = id; expressions.push_back(myExpr); } - return xorRemovedOddTrues ? NOT(id) : id; + if (xorRemovedOddTrues) + id = NOT(id); + + addhash(__LINE__); + addhash(id); + + return id; } void ezSAT::lookup_literal(int id, std::string &name) const @@ -281,7 +312,7 @@ std::string ezSAT::to_string(int id) const int ezSAT::eval(int id, const std::vector &values) const { if (id > 0) { - if (id <= int(values.size()) && (values[id-1] == TRUE || values[id-1] == FALSE || values[id-1] == 0)) + if (id <= int(values.size()) && (values[id-1] == CONST_TRUE || values[id-1] == CONST_FALSE || values[id-1] == 0)) return values[id-1]; return 0; } @@ -295,39 +326,39 @@ int ezSAT::eval(int id, const std::vector &values) const case OpNot: assert(args.size() == 1); a = eval(args[0], values); - if (a == TRUE) - return FALSE; - if (a == FALSE) - return TRUE; + if (a == CONST_TRUE) + return CONST_FALSE; + if (a == CONST_FALSE) + return CONST_TRUE; return 0; case OpAnd: - a = TRUE; + a = CONST_TRUE; for (auto arg : args) { b = eval(arg, values); - if (b != TRUE && b != FALSE) + if (b != CONST_TRUE && b != CONST_FALSE) a = 0; - if (b == FALSE) - return FALSE; + if (b == CONST_FALSE) + return CONST_FALSE; } return a; case OpOr: - a = FALSE; + a = CONST_FALSE; for (auto arg : args) { b = eval(arg, values); - if (b != TRUE && b != FALSE) + if (b != CONST_TRUE && b != CONST_FALSE) a = 0; - if (b == TRUE) - return TRUE; + if (b == CONST_TRUE) + return CONST_TRUE; } return a; case OpXor: - a = FALSE; + a = CONST_FALSE; for (auto arg : args) { b = eval(arg, values); - if (b != TRUE && b != FALSE) + if (b != CONST_TRUE && b != CONST_FALSE) return 0; - if (b == TRUE) - a = a == TRUE ? FALSE : TRUE; + if (b == CONST_TRUE) + a = a == CONST_TRUE ? CONST_FALSE : CONST_TRUE; } return a; case OpIFF: @@ -335,18 +366,18 @@ int ezSAT::eval(int id, const std::vector &values) const a = eval(args[0], values); for (auto arg : args) { b = eval(arg, values); - if (b != TRUE && b != FALSE) + if (b != CONST_TRUE && b != CONST_FALSE) return 0; if (b != a) - return FALSE; + return CONST_FALSE; } - return TRUE; + return CONST_TRUE; case OpITE: assert(args.size() == 3); a = eval(args[0], values); - if (a == TRUE) + if (a == CONST_TRUE) return eval(args[1], values); - if (a == FALSE) + if (a == CONST_FALSE) return eval(args[2], values); return 0; default: @@ -375,6 +406,9 @@ bool ezSAT::eliminated(int) void ezSAT::assume(int id) { + addhash(__LINE__); + addhash(id); + if (id < 0) { assert(0 < -id && -id <= int(expressions.size())); @@ -417,6 +451,10 @@ void ezSAT::assume(int id) void ezSAT::add_clause(const std::vector &args) { + addhash(__LINE__); + for (auto arg : args) + addhash(arg); + cnfClauses.push_back(args); cnfClausesCount++; } @@ -490,13 +528,13 @@ int ezSAT::bound(int id) const std::string ezSAT::cnfLiteralInfo(int idx) const { - for (size_t i = 0; i < cnfLiteralVariables.size(); i++) { + for (int i = 0; i < int(cnfLiteralVariables.size()); i++) { if (cnfLiteralVariables[i] == idx) return to_string(i+1); if (cnfLiteralVariables[i] == -idx) return "NOT " + to_string(i+1); } - for (size_t i = 0; i < cnfExpressionVariables.size(); i++) { + for (int i = 0; i < int(cnfExpressionVariables.size()); i++) { if (cnfExpressionVariables[i] == idx) return to_string(-i-1); if (cnfExpressionVariables[i] == -idx) @@ -507,6 +545,10 @@ std::string ezSAT::cnfLiteralInfo(int idx) const int ezSAT::bind(int id, bool auto_freeze) { + addhash(__LINE__); + addhash(id); + addhash(auto_freeze); + if (id >= 0) { assert(0 < id && id <= int(literals.size())); cnfLiteralVariables.resize(literals.size()); @@ -516,9 +558,9 @@ int ezSAT::bind(int id, bool auto_freeze) } if (cnfLiteralVariables[id-1] == 0) { cnfLiteralVariables[id-1] = ++cnfVariableCount; - if (id == TRUE) + if (id == CONST_TRUE) add_clause(+cnfLiteralVariables[id-1]); - if (id == FALSE) + if (id == CONST_FALSE) add_clause(-cnfLiteralVariables[id-1]); } return cnfLiteralVariables[id-1]; @@ -549,10 +591,13 @@ int ezSAT::bind(int id, bool auto_freeze) while (args.size() > 1) { std::vector newArgs; for (int i = 0; i < int(args.size()); i += 2) - if (i+1 == int(args.size())) + if (i+1 == int(args.size())) { newArgs.push_back(args[i]); - else - newArgs.push_back(OR(AND(args[i], NOT(args[i+1])), AND(NOT(args[i]), args[i+1]))); + } else { + int sub1 = AND(args[i], NOT(args[i+1])); + int sub2 = AND(NOT(args[i]), args[i+1]); + newArgs.push_back(OR(sub1, sub2)); + } args.swap(newArgs); } idx = bind(args.at(0), false); @@ -563,12 +608,16 @@ int ezSAT::bind(int id, bool auto_freeze) std::vector invArgs; for (auto arg : args) invArgs.push_back(NOT(arg)); - idx = bind(OR(expression(OpAnd, args), expression(OpAnd, invArgs)), false); + int sub1 = expression(OpAnd, args); + int sub2 = expression(OpAnd, invArgs); + idx = bind(OR(sub1, sub2), false); goto assign_idx; } if (op == OpITE) { - idx = bind(OR(AND(args[0], args[1]), AND(NOT(args[0]), args[2])), false); + int sub1 = AND(args[0], args[1]); + int sub2 = AND(NOT(args[0]), args[2]); + idx = bind(OR(sub1, sub2), false); goto assign_idx; } @@ -638,7 +687,7 @@ std::vector ezSAT::vec_const(const std::vector &bits) { std::vector vec; for (auto bit : bits) - vec.push_back(bit ? TRUE : FALSE); + vec.push_back(bit ? CONST_TRUE : CONST_FALSE); return vec; } @@ -646,7 +695,7 @@ std::vector ezSAT::vec_const_signed(int64_t value, int numBits) { std::vector vec; for (int i = 0; i < numBits; i++) - vec.push_back(((value >> i) & 1) != 0 ? TRUE : FALSE); + vec.push_back(((value >> i) & 1) != 0 ? CONST_TRUE : CONST_FALSE); return vec; } @@ -654,7 +703,7 @@ std::vector ezSAT::vec_const_unsigned(uint64_t value, int numBits) { std::vector vec; for (int i = 0; i < numBits; i++) - vec.push_back(((value >> i) & 1) != 0 ? TRUE : FALSE); + vec.push_back(((value >> i) & 1) != 0 ? CONST_TRUE : CONST_FALSE); return vec; } @@ -669,8 +718,9 @@ std::vector ezSAT::vec_var(int numBits) std::vector ezSAT::vec_var(std::string name, int numBits) { std::vector vec; - for (int i = 0; i < numBits; i++) - vec.push_back(VAR(name + "[" + std::to_string(i) + "]")); + for (int i = 0; i < numBits; i++) { + vec.push_back(VAR(name + my_int_to_string(i))); + } return vec; } @@ -679,7 +729,7 @@ std::vector ezSAT::vec_cast(const std::vector &vec1, int toBits, bool std::vector vec; for (int i = 0; i < toBits; i++) if (i >= int(vec1.size())) - vec.push_back(signExtend ? vec1.back() : FALSE); + vec.push_back(signExtend ? vec1.back() : CONST_FALSE); else vec.push_back(vec1[i]); return vec; @@ -807,7 +857,7 @@ std::vector ezSAT::vec_add(const std::vector &vec1, const std::vector< { assert(vec1.size() == vec2.size()); std::vector vec(vec1.size()); - int carry = FALSE; + int carry = CONST_FALSE; for (int i = 0; i < int(vec1.size()); i++) fulladder(this, vec1[i], vec2[i], carry, carry, vec[i]); @@ -831,7 +881,7 @@ std::vector ezSAT::vec_sub(const std::vector &vec1, const std::vector< { assert(vec1.size() == vec2.size()); std::vector vec(vec1.size()); - int carry = TRUE; + int carry = CONST_TRUE; for (int i = 0; i < int(vec1.size()); i++) fulladder(this, vec1[i], NOT(vec2[i]), carry, carry, vec[i]); @@ -853,15 +903,15 @@ std::vector ezSAT::vec_sub(const std::vector &vec1, const std::vector< std::vector ezSAT::vec_neg(const std::vector &vec) { - std::vector zero(vec.size(), FALSE); + std::vector zero(vec.size(), CONST_FALSE); return vec_sub(zero, vec); } void ezSAT::vec_cmp(const std::vector &vec1, const std::vector &vec2, int &carry, int &overflow, int &sign, int &zero) { assert(vec1.size() == vec2.size()); - carry = TRUE; - zero = FALSE; + carry = CONST_TRUE; + zero = CONST_FALSE; for (int i = 0; i < int(vec1.size()); i++) { overflow = carry; fulladder(this, vec1[i], NOT(vec2[i]), carry, carry, sign); @@ -954,11 +1004,11 @@ std::vector ezSAT::vec_shl(const std::vector &vec1, int shift, bool si for (int i = 0; i < int(vec1.size()); i++) { int j = i-shift; if (int(vec1.size()) <= j) - vec.push_back(signExtend ? vec1.back() : FALSE); + vec.push_back(signExtend ? vec1.back() : CONST_FALSE); else if (0 <= j) vec.push_back(vec1[j]); else - vec.push_back(FALSE); + vec.push_back(CONST_FALSE); } return vec; } @@ -1005,10 +1055,10 @@ std::vector ezSAT::vec_shift_right(const std::vector &vec1, const std: int vec2_bits = std::min(my_clog2(vec1.size()) + (vec2_signed ? 1 : 0), int(vec2.size())); std::vector overflow_bits(vec2.begin() + vec2_bits, vec2.end()); - int overflow_left = FALSE, overflow_right = FALSE; + int overflow_left = CONST_FALSE, overflow_right = CONST_FALSE; if (vec2_signed) { - int overflow = FALSE; + int overflow = CONST_FALSE; for (auto bit : overflow_bits) overflow = OR(overflow, XOR(bit, vec2[vec2_bits-1])); overflow_left = AND(overflow, NOT(vec2.back())); @@ -1046,7 +1096,7 @@ std::vector ezSAT::vec_shift_right(const std::vector &vec1, const std: std::vector ezSAT::vec_shift_left(const std::vector &vec1, const std::vector &vec2, bool vec2_signed, int extend_left, int extend_right) { // vec2_signed is not implemented in vec_shift_left() yet - assert(vec2_signed == false); + if (vec2_signed) assert(vec2_signed == false); int vec2_bits = std::min(my_clog2(vec1.size()), int(vec2.size())); @@ -1105,7 +1155,7 @@ int64_t ezSAT::vec_model_get_signed(const std::vector &modelExpressions, co for (int i = 0; i < 64; i++) { int j = i < int(vec1.size()) ? i : vec1.size()-1; if (modelMap.at(vec1[j])) - value |= 1 << i; + value |= int64_t(1) << i; } return value; } @@ -1119,7 +1169,7 @@ uint64_t ezSAT::vec_model_get_unsigned(const std::vector &modelExpressions, modelMap[modelExpressions[i]] = modelValues[i]; for (int i = 0; i < int(vec1.size()); i++) if (modelMap.at(vec1[i])) - value |= 1 << i; + value |= uint64_t(1) << i; return value; } @@ -1195,7 +1245,7 @@ void ezSAT::printDIMACS(FILE *f, bool verbose) const fprintf(f, "c mapping of variables to expressions:\n"); for (int i = 0; i < int(cnfExpressionVariables.size()); i++) if (cnfExpressionVariables[i] != 0) - fprintf(f, "c %*d: %s\n", digits, cnfExpressionVariables[i], to_string(-i-1).c_str()); + fprintf(f, "c %*d: %d\n", digits, cnfExpressionVariables[i], -i-1); if (mode_keep_cnf()) { fprintf(f, "c\n"); @@ -1243,7 +1293,7 @@ static std::string expression2str(const std::pair> } text += ":"; for (auto it : data.second) - text += " " + std::to_string(it); + text += " " + my_int_to_string(it); return text; } @@ -1330,7 +1380,7 @@ int ezSAT::manyhot(const std::vector &vec, int min_hot, int max_hot) for (int i = -1; i < N; i++) for (int j = -1; j < M; j++) - x[std::pair(i,j)] = j < 0 ? TRUE : i < 0 ? FALSE : literal(); + x[std::pair(i,j)] = j < 0 ? CONST_TRUE : i < 0 ? CONST_FALSE : literal(); for (int i = 0; i < N; i++) for (int j = 0; j < M; j++) { @@ -1358,7 +1408,7 @@ int ezSAT::manyhot(const std::vector &vec, int min_hot, int max_hot) int ezSAT::ordered(const std::vector &vec1, const std::vector &vec2, bool allow_equal) { std::vector formula; - int last_x = FALSE; + int last_x = CONST_FALSE; assert(vec1.size() == vec2.size()); for (size_t i = 0; i < vec1.size(); i++) @@ -1366,7 +1416,7 @@ int ezSAT::ordered(const std::vector &vec1, const std::vector &vec2, b int a = vec1[i], b = vec2[i]; formula.push_back(OR(NOT(a), b, last_x)); - int next_x = i+1 < vec1.size() ? literal() : allow_equal ? FALSE : TRUE; + int next_x = i+1 < vec1.size() ? literal() : allow_equal ? CONST_FALSE : CONST_TRUE; formula.push_back(OR(a, b, last_x, NOT(next_x))); formula.push_back(OR(NOT(a), NOT(b), last_x, NOT(next_x))); last_x = next_x; diff --git a/libs/ezsat/ezsat.h b/libs/ezsat/ezsat.h index c5ef6b0a..0faaa6b8 100644 --- a/libs/ezsat/ezsat.h +++ b/libs/ezsat/ezsat.h @@ -25,6 +25,7 @@ #include #include #include +#include class ezSAT { @@ -34,7 +35,7 @@ class ezSAT // the number zero is not used as valid token number and is used to encode // unused parameters for the functions. // - // positive numbers are literals, with 1 = TRUE and 2 = FALSE; + // positive numbers are literals, with 1 = CONST_TRUE and 2 = CONST_FALSE; // // negative numbers are non-literal expressions. each expression is represented // by an operator id and a list of expressions (literals or non-literals). @@ -44,8 +45,8 @@ public: OpNot, OpAnd, OpOr, OpXor, OpIFF, OpITE }; - static const int TRUE; - static const int FALSE; + static const int CONST_TRUE; + static const int CONST_FALSE; private: bool flag_keep_cnf; @@ -82,6 +83,9 @@ public: ezSAT(); virtual ~ezSAT(); + unsigned int statehash; + void addhash(unsigned int); + void keep_cnf() { flag_keep_cnf = true; } void non_incremental() { flag_non_incremental = true; } @@ -159,6 +163,7 @@ public: virtual void freeze(int id); virtual bool eliminated(int idx); void assume(int id); + void assume(int id, int context_id) { assume(OR(id, NOT(context_id))); } int bind(int id, bool auto_freeze = true); int bound(int id) const; diff --git a/libs/sha1/sha1.cpp b/libs/sha1/sha1.cpp index dc86b2ce..51bbd85c 100644 --- a/libs/sha1/sha1.cpp +++ b/libs/sha1/sha1.cpp @@ -1,74 +1,76 @@ /* sha1.cpp - source code of - + ============ SHA-1 in C++ ============ - + 100% Public Domain. - + Original C Code -- Steve Reid Small changes to fit into bglibs -- Bruce Guenter Translation to simpler C++ Code -- Volker Grabsch + Fixing bugs and improving style + -- Eugene Hopkinson */ - + #include "sha1.h" #include #include #include - + /* Help macros */ #define SHA1_ROL(value, bits) (((value) << (bits)) | (((value) & 0xffffffff) >> (32 - (bits)))) #define SHA1_BLK(i) (block[i&15] = SHA1_ROL(block[(i+13)&15] ^ block[(i+8)&15] ^ block[(i+2)&15] ^ block[i&15],1)) - + /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define SHA1_R0(v,w,x,y,z,i) z += ((w&(x^y))^y) + block[i] + 0x5a827999 + SHA1_ROL(v,5); w=SHA1_ROL(w,30); #define SHA1_R1(v,w,x,y,z,i) z += ((w&(x^y))^y) + SHA1_BLK(i) + 0x5a827999 + SHA1_ROL(v,5); w=SHA1_ROL(w,30); #define SHA1_R2(v,w,x,y,z,i) z += (w^x^y) + SHA1_BLK(i) + 0x6ed9eba1 + SHA1_ROL(v,5); w=SHA1_ROL(w,30); #define SHA1_R3(v,w,x,y,z,i) z += (((w|x)&y)|(w&x)) + SHA1_BLK(i) + 0x8f1bbcdc + SHA1_ROL(v,5); w=SHA1_ROL(w,30); #define SHA1_R4(v,w,x,y,z,i) z += (w^x^y) + SHA1_BLK(i) + 0xca62c1d6 + SHA1_ROL(v,5); w=SHA1_ROL(w,30); - + SHA1::SHA1() { reset(); } - - + + void SHA1::update(const std::string &s) { std::istringstream is(s); update(is); } - - + + void SHA1::update(std::istream &is) { std::string rest_of_buffer; read(is, rest_of_buffer, BLOCK_BYTES - buffer.size()); buffer += rest_of_buffer; - + while (is) { - uint32 block[BLOCK_INTS]; + uint32_t block[BLOCK_INTS]; buffer_to_block(buffer, block); transform(block); read(is, buffer, BLOCK_BYTES); } } - - + + /* * Add padding and return the message digest. */ - + std::string SHA1::final() { /* Total number of hashed bits */ - uint64 total_bits = (transforms*BLOCK_BYTES + buffer.size()) * 8; - + uint64_t total_bits = (transforms*BLOCK_BYTES + buffer.size()) * 8; + /* Padding */ buffer += 0x80; unsigned int orig_size = buffer.size(); @@ -76,10 +78,10 @@ std::string SHA1::final() { buffer += (char)0x00; } - - uint32 block[BLOCK_INTS]; + + uint32_t block[BLOCK_INTS]; buffer_to_block(buffer, block); - + if (orig_size > BLOCK_BYTES - 8) { transform(block); @@ -88,12 +90,12 @@ std::string SHA1::final() block[i] = 0; } } - - /* Append total_bits, split this uint64 into two uint32 */ + + /* Append total_bits, split this uint64_t into two uint32_t */ block[BLOCK_INTS - 1] = total_bits; block[BLOCK_INTS - 2] = (total_bits >> 32); transform(block); - + /* Hex std::string */ std::ostringstream result; for (unsigned int i = 0; i < DIGEST_INTS; i++) @@ -101,14 +103,14 @@ std::string SHA1::final() result << std::hex << std::setfill('0') << std::setw(8); result << (digest[i] & 0xffffffff); } - + /* Reset for next run */ reset(); - + return result.str(); } - - + + std::string SHA1::from_file(const std::string &filename) { std::ifstream stream(filename.c_str(), std::ios::binary); @@ -116,8 +118,8 @@ std::string SHA1::from_file(const std::string &filename) checksum.update(stream); return checksum.final(); } - - + + void SHA1::reset() { /* SHA1 initialization constants */ @@ -126,27 +128,27 @@ void SHA1::reset() digest[2] = 0x98badcfe; digest[3] = 0x10325476; digest[4] = 0xc3d2e1f0; - + /* Reset counters */ transforms = 0; buffer = ""; } - - + + /* * Hash a single 512-bit block. This is the core of the algorithm. */ - -void SHA1::transform(uint32 block[BLOCK_BYTES]) + +void SHA1::transform(uint32_t block[BLOCK_BYTES]) { /* Copy digest[] to working vars */ - uint32 a = digest[0]; - uint32 b = digest[1]; - uint32 c = digest[2]; - uint32 d = digest[3]; - uint32 e = digest[4]; - - + uint32_t a = digest[0]; + uint32_t b = digest[1]; + uint32_t c = digest[2]; + uint32_t d = digest[3]; + uint32_t e = digest[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ SHA1_R0(a,b,c,d,e, 0); SHA1_R0(e,a,b,c,d, 1); @@ -228,22 +230,22 @@ void SHA1::transform(uint32 block[BLOCK_BYTES]) SHA1_R4(d,e,a,b,c,77); SHA1_R4(c,d,e,a,b,78); SHA1_R4(b,c,d,e,a,79); - + /* Add the working vars back into digest[] */ digest[0] += a; digest[1] += b; digest[2] += c; digest[3] += d; digest[4] += e; - + /* Count the number of transformations */ transforms++; } - - -void SHA1::buffer_to_block(const std::string &buffer, uint32 block[BLOCK_BYTES]) + + +void SHA1::buffer_to_block(const std::string &buffer, uint32_t block[BLOCK_INTS]) { - /* Convert the std::string (byte buffer) to a uint32 array (MSB) */ + /* Convert the std::string (byte buffer) to a uint32_t array (MSB) */ for (unsigned int i = 0; i < BLOCK_INTS; i++) { block[i] = (buffer[4*i+3] & 0xff) @@ -252,16 +254,19 @@ void SHA1::buffer_to_block(const std::string &buffer, uint32 block[BLOCK_BYTES]) | (buffer[4*i+0] & 0xff)<<24; } } - - -void SHA1::read(std::istream &is, std::string &s, int max) + + +void SHA1::read(std::istream &is, std::string &s, size_t max) { - char sbuf[max]; + char* sbuf = new char[max]; + is.read(sbuf, max); s.assign(sbuf, is.gcount()); + + delete[] sbuf; } - - + + std::string sha1(const std::string &string) { SHA1 checksum; diff --git a/libs/sha1/sha1.h b/libs/sha1/sha1.h index 15edee12..9f526376 100644 --- a/libs/sha1/sha1.h +++ b/libs/sha1/sha1.h @@ -1,27 +1,30 @@ /* sha1.h - header of - + ============ SHA-1 in C++ ============ - + 100% Public Domain. - + Original C Code -- Steve Reid Small changes to fit into bglibs -- Bruce Guenter Translation to simpler C++ Code -- Volker Grabsch + Fixing bugs and improving style + -- Eugene Hopkinson */ - + #ifndef SHA1_HPP #define SHA1_HPP - - + + #include #include - +#include + class SHA1 { public: @@ -30,28 +33,25 @@ public: void update(std::istream &is); std::string final(); static std::string from_file(const std::string &filename); - + private: - typedef unsigned long int uint32; /* just needs to be at least 32bit */ - typedef unsigned long long uint64; /* just needs to be at least 64bit */ - static const unsigned int DIGEST_INTS = 5; /* number of 32bit integers per SHA1 digest */ static const unsigned int BLOCK_INTS = 16; /* number of 32bit integers per SHA1 block */ static const unsigned int BLOCK_BYTES = BLOCK_INTS * 4; - - uint32 digest[DIGEST_INTS]; + + uint32_t digest[DIGEST_INTS]; std::string buffer; - uint64 transforms; - + uint64_t transforms; + void reset(); - void transform(uint32 block[BLOCK_BYTES]); - - static void buffer_to_block(const std::string &buffer, uint32 block[BLOCK_BYTES]); - static void read(std::istream &is, std::string &s, int max); + void transform(uint32_t block[BLOCK_BYTES]); + + static void read(std::istream &is, std::string &s, size_t max); + static void buffer_to_block(const std::string &buffer, uint32_t block[BLOCK_INTS]); }; - + std::string sha1(const std::string &string); - - - + + + #endif /* SHA1_HPP */ diff --git a/libs/subcircuit/subcircuit.cc b/libs/subcircuit/subcircuit.cc index 84f23d63..cf14df0a 100644 --- a/libs/subcircuit/subcircuit.cc +++ b/libs/subcircuit/subcircuit.cc @@ -34,6 +34,7 @@ using namespace SubCircuit; +#ifndef _YOSYS_ static std::string my_stringf(const char *fmt, ...) { std::string string; @@ -52,6 +53,9 @@ static std::string my_stringf(const char *fmt, ...) return string; } +#else +# define my_stringf YOSYS_NAMESPACE_PREFIX stringf +#endif SubCircuit::Graph::Graph(const Graph &other, const std::vector &otherNodes) { diff --git a/manual/APPNOTE_010_Verilog_to_BLIF.tex b/manual/APPNOTE_010_Verilog_to_BLIF.tex index 9ee87bc4..0f521fb0 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. @@ -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/cmos_00.dot b/manual/APPNOTE_011_Design_Investigation/cmos_00.dot index 85ca7849..49c63008 100644 --- a/manual/APPNOTE_011_Design_Investigation/cmos_00.dot +++ b/manual/APPNOTE_011_Design_Investigation/cmos_00.dot @@ -31,4 +31,4 @@ n5:e -> c11:p7:w [color="black", label=""]; n6:e -> x0:s0:w [color="black", label=""]; n6:e -> x1:s0:w [color="black", label=""]; n6:e -> x2:s0:w [color="black", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/cmos_01.dot b/manual/APPNOTE_011_Design_Investigation/cmos_01.dot index de9af170..ea6f4403 100644 --- a/manual/APPNOTE_011_Design_Investigation/cmos_01.dot +++ b/manual/APPNOTE_011_Design_Investigation/cmos_01.dot @@ -20,4 +20,4 @@ n5:e -> c12:p8:w [color="black", label=""]; c15:p10:e -> n6:w [color="black", label=""]; c14:p10:e -> n7:w [color="black", label=""]; n7:e -> c15:p9:w [color="black", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/example_00.dot b/manual/APPNOTE_011_Design_Investigation/example_00.dot index b38862c4..1e23ed0e 100644 --- a/manual/APPNOTE_011_Design_Investigation/example_00.dot +++ b/manual/APPNOTE_011_Design_Investigation/example_00.dot @@ -20,4 +20,4 @@ n7:e -> p1:w [color="black", label=""]; p1:e -> n8:w [color="black", style="setlinewidth(3)", label=""]; n8:e -> p1:w [color="black", style="setlinewidth(3)", label=""]; v0:e -> c14:p9:w [color="black", style="setlinewidth(3)", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/example_01.dot b/manual/APPNOTE_011_Design_Investigation/example_01.dot index e2e3f02d..e89292b5 100644 --- a/manual/APPNOTE_011_Design_Investigation/example_01.dot +++ b/manual/APPNOTE_011_Design_Investigation/example_01.dot @@ -30,4 +30,4 @@ n8:e -> c21:p19:w [color="black", label=""]; n8:e -> x1:w:w [color="black", label=""]; n9:e -> c18:p15:w [color="black", label=""]; v0:e -> c21:p11:w [color="black", style="setlinewidth(3)", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/example_02.dot b/manual/APPNOTE_011_Design_Investigation/example_02.dot index 4b57f89c..f950ed2e 100644 --- a/manual/APPNOTE_011_Design_Investigation/example_02.dot +++ b/manual/APPNOTE_011_Design_Investigation/example_02.dot @@ -17,4 +17,4 @@ n5:e -> c17:p16:w [color="black", label=""]; n6:e -> c15:p12:w [color="black", label=""]; c15:p14:e -> n7:w [color="black", style="setlinewidth(3)", label=""]; n7:e -> c17:p8:w [color="black", style="setlinewidth(3)", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/example_03.dot b/manual/APPNOTE_011_Design_Investigation/example_03.dot index 6c00c29a..e19d24af 100644 --- a/manual/APPNOTE_011_Design_Investigation/example_03.dot +++ b/manual/APPNOTE_011_Design_Investigation/example_03.dot @@ -8,4 +8,4 @@ c4 [ shape=record, label="{{ A| B}|$2\n$add|{ Y}}" ]; v0:e -> c4:p1:w [color="black", label=""]; v1:e -> c4:p2:w [color="black", label=""]; c4:p3:e -> v2:w [color="black", style="setlinewidth(3)", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/memdemo_00.dot b/manual/APPNOTE_011_Design_Investigation/memdemo_00.dot index 9e81edbc..0336a9aa 100644 --- a/manual/APPNOTE_011_Design_Investigation/memdemo_00.dot +++ b/manual/APPNOTE_011_Design_Investigation/memdemo_00.dot @@ -135,4 +135,4 @@ v6:e -> c47:p34:w [color="black", label=""]; v7:e -> c48:p33:w [color="black", style="setlinewidth(3)", label=""]; v8:e -> c49:p33:w [color="black", style="setlinewidth(3)", label=""]; v9:e -> c50:p33:w [color="black", style="setlinewidth(3)", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/memdemo_01.dot b/manual/APPNOTE_011_Design_Investigation/memdemo_01.dot index bbd7fcc6..2ad92c78 100644 --- a/manual/APPNOTE_011_Design_Investigation/memdemo_01.dot +++ b/manual/APPNOTE_011_Design_Investigation/memdemo_01.dot @@ -26,4 +26,4 @@ v0:e -> c13:p11:w [color="black", label=""]; v1:e -> c14:p11:w [color="black", label=""]; v2:e -> c15:p11:w [color="black", label=""]; v3:e -> c19:p16:w [color="black", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/splice.dot b/manual/APPNOTE_011_Design_Investigation/splice.dot index 1bcd55b4..4657feed 100644 --- a/manual/APPNOTE_011_Design_Investigation/splice.dot +++ b/manual/APPNOTE_011_Design_Investigation/splice.dot @@ -36,4 +36,4 @@ x1:s0:e -> n8:w [color="black", style="setlinewidth(3)", label=""]; x3:s0:e -> n8:w [color="black", style="setlinewidth(3)", label=""]; x3:s1:e -> n8:w [color="black", style="setlinewidth(3)", label=""]; x6:s0:e -> n8:w [color="black", style="setlinewidth(3)", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/submod_00.dot b/manual/APPNOTE_011_Design_Investigation/submod_00.dot index 4567dfb4..2e55268e 100644 --- a/manual/APPNOTE_011_Design_Investigation/submod_00.dot +++ b/manual/APPNOTE_011_Design_Investigation/submod_00.dot @@ -42,4 +42,4 @@ c21:p8:e -> n8:w [color="black", style="setlinewidth(3)", label=""]; n8:e -> c20:p8:w [color="black", style="setlinewidth(3)", label=""]; c21:p9:e -> n9:w [color="black", style="setlinewidth(3)", label=""]; n9:e -> c20:p9:w [color="black", style="setlinewidth(3)", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/submod_01.dot b/manual/APPNOTE_011_Design_Investigation/submod_01.dot index b1daae23..f8f8c008 100644 --- a/manual/APPNOTE_011_Design_Investigation/submod_01.dot +++ b/manual/APPNOTE_011_Design_Investigation/submod_01.dot @@ -84,4 +84,4 @@ v4:e -> c35:p24:w [color="black", style="setlinewidth(3)", label=""]; v5:e -> c36:p24:w [color="black", style="setlinewidth(3)", label=""]; v6:e -> c37:p24:w [color="black", style="setlinewidth(3)", label=""]; v7:e -> c38:p24:w [color="black", style="setlinewidth(3)", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/submod_02.dot b/manual/APPNOTE_011_Design_Investigation/submod_02.dot index 68266db9..1a672c48 100644 --- a/manual/APPNOTE_011_Design_Investigation/submod_02.dot +++ b/manual/APPNOTE_011_Design_Investigation/submod_02.dot @@ -30,4 +30,4 @@ n8:e -> c17:p12:w [color="black", style="setlinewidth(3)", label=""]; n9:e -> x0:s0:w [color="black", label=""]; n9:e -> x1:s0:w [color="black", label=""]; n9:e -> x2:s0:w [color="black", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/submod_03.dot b/manual/APPNOTE_011_Design_Investigation/submod_03.dot index 92ef5759..0dbbe3ba 100644 --- a/manual/APPNOTE_011_Design_Investigation/submod_03.dot +++ b/manual/APPNOTE_011_Design_Investigation/submod_03.dot @@ -23,4 +23,4 @@ x1:s1:e -> n5:w [color="black", style="setlinewidth(3)", label=""]; n6:e -> x2:s1:w [color="black", style="setlinewidth(3)", label=""]; n7:e -> x2:s0:w [color="black", style="setlinewidth(3)", label=""]; v0:e -> c13:p8:w [color="black", style="setlinewidth(3)", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_00.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_00.dot index 6f9b7d3d..06522dcc 100644 --- a/manual/APPNOTE_011_Design_Investigation/sumprod_00.dot +++ b/manual/APPNOTE_011_Design_Investigation/sumprod_00.dot @@ -15,4 +15,4 @@ c4:p3:e -> v2:w [color="black", style="setlinewidth(3)", label=""]; v3:e -> c5:p1:w [color="black", style="setlinewidth(3)", label=""]; v4:e -> c5:p2:w [color="black", style="setlinewidth(3)", label=""]; c5:p3:e -> v5:w [color="black", style="setlinewidth(3)", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_01.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_01.dot index d0252270..aefe7a6d 100644 --- a/manual/APPNOTE_011_Design_Investigation/sumprod_01.dot +++ b/manual/APPNOTE_011_Design_Investigation/sumprod_01.dot @@ -12,4 +12,4 @@ n2:e -> c9:p6:w [color="black", style="setlinewidth(3)", label=""]; n3:e -> c9:p7:w [color="black", style="setlinewidth(3)", label=""]; n4:e -> c10:p7:w [color="black", style="setlinewidth(3)", label=""]; c10:p8:e -> n5:w [color="black", style="setlinewidth(3)", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_02.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_02.dot index af87651b..4646c994 100644 --- a/manual/APPNOTE_011_Design_Investigation/sumprod_02.dot +++ b/manual/APPNOTE_011_Design_Investigation/sumprod_02.dot @@ -2,4 +2,4 @@ digraph "sumprod" { rankdir="LR"; remincross=true; n1 [ shape=octagon, label="prod", color="black", fontcolor="black" ]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_03.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_03.dot index 21155135..dcfea2b5 100644 --- a/manual/APPNOTE_011_Design_Investigation/sumprod_03.dot +++ b/manual/APPNOTE_011_Design_Investigation/sumprod_03.dot @@ -8,4 +8,4 @@ c5 [ shape=record, label="{{ A| B}|$4\n$mul|{ Y}}" ]; c5:p4:e -> n1:w [color="black", style="setlinewidth(3)", label=""]; v0:e -> c5:p2:w [color="black", style="setlinewidth(3)", label=""]; v1:e -> c5:p3:w [color="black", style="setlinewidth(3)", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_04.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_04.dot index 5223577e..e77c41aa 100644 --- a/manual/APPNOTE_011_Design_Investigation/sumprod_04.dot +++ b/manual/APPNOTE_011_Design_Investigation/sumprod_04.dot @@ -8,4 +8,4 @@ n1 [ shape=diamond, label="$3_Y" ]; n1:e -> c7:p4:w [color="black", style="setlinewidth(3)", label=""]; n2:e -> c7:p5:w [color="black", style="setlinewidth(3)", label=""]; c7:p6:e -> n3:w [color="black", style="setlinewidth(3)", label=""]; -}; +} diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_05.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_05.dot index 45d00134..b5444129 100644 --- a/manual/APPNOTE_011_Design_Investigation/sumprod_05.dot +++ b/manual/APPNOTE_011_Design_Investigation/sumprod_05.dot @@ -12,4 +12,4 @@ n2:e -> c8:p5:w [color="black", style="setlinewidth(3)", label=""]; c8:p6:e -> n3:w [color="black", style="setlinewidth(3)", label=""]; v0:e -> c7:p4:w [color="black", style="setlinewidth(3)", label=""]; v1:e -> c7:p5:w [color="black", style="setlinewidth(3)", label=""]; -}; +} diff --git a/manual/CHAPTER_Appnotes.tex b/manual/CHAPTER_Appnotes.tex index 959aabd2..2abfa85d 100644 --- a/manual/CHAPTER_Appnotes.tex +++ b/manual/CHAPTER_Appnotes.tex @@ -2,11 +2,24 @@ \chapter{Application Notes} \label{chapter:appnotes} -\begin{fixme} -This appendix will cover some typical use-cases of Yosys in the form of application notes. -\end{fixme} +% \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{Reconfigurable Coarse-Grain Synthesis using Intersynth} -\section{Synthesizing using a Cell Library in Liberty Format} -\section{Reverse Engeneering the MOS6502 from an NMOS Transistor Netlist} -\section{Reconfigurable Coarse-Grain Synthesis using Intersynth} +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 +\end{itemize} + +\eject\label{app:010} +\includepdf[pages=-,pagecommand=\thispagestyle{plain}]{APPNOTE_010_Verilog_to_BLIF.pdf} + +\eject\label{app:011} +\includepdf[pages=-,pagecommand=\thispagestyle{plain}]{APPNOTE_011_Design_Investigation.pdf} diff --git a/manual/CHAPTER_Auxprogs.tex b/manual/CHAPTER_Auxprogs.tex index cce3741c..a0089382 100644 --- a/manual/CHAPTER_Auxprogs.tex +++ b/manual/CHAPTER_Auxprogs.tex @@ -17,3 +17,9 @@ The {\tt yosys-filterlib} tool is a small utility that can be used to strip or extract information from a Liberty file. See Sec.~\ref{sec:techmap_extern} for details. +\section{yosys-abc} + +This is a unmodified copy of ABC \citeweblink{ABC}. Not all versions of Yosys +work with all versions of ABC. So Yosys comes with its own yosys-abc to avoid +compatibility issues between the two. + diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex index 64d3633e..c9bf978a 100644 --- a/manual/CHAPTER_CellLib.tex +++ b/manual/CHAPTER_CellLib.tex @@ -357,7 +357,7 @@ Add a brief description of the {\tt \$fsm} cell type. For gate level logic networks, fixed function single bit cells are used that do not provide any parameters. -Simulation models for these cells can be found in the file {\tt techlibs/common/stdcells\_sim.v} in the Yosys +Simulation models for these cells can be found in the file {\tt techlibs/common/simcells.v} in the Yosys source tree. \begin{table}[t] @@ -417,7 +417,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} cells. +Add information about {\tt \$assert}, {\tt \$assume}, and {\tt \$equiv} cells. \end{fixme} \begin{fixme} @@ -428,6 +428,14 @@ Add information about {\tt \$slice} and {\tt \$concat} cells. Add information about {\tt \$alu}, {\tt \$macc}, {\tt \$fa}, and {\tt \$lcu} cells. \end{fixme} +\begin{fixme} +Add information about {\tt \$dffe}, {\tt \$dffsr}, {\tt \$dlatch}, and {\tt \$dlatchsr} cells. +\end{fixme} + +\begin{fixme} +Add information about {\tt \$\_DFFE\_??\_}, {\tt \$\_DFFSR\_???\_}, {\tt \$\_DLATCH\_?\_}, and {\tt \$\_DLATCHSR\_???\_} cells. +\end{fixme} + \begin{fixme} Add information about {\tt \$\_NAND\_}, {\tt \$\_NOR\_}, {\tt \$\_XNOR\_}, {\tt \$\_AOI3\_}, {\tt \$\_OAI3\_}, {\tt \$\_AOI4\_}, and {\tt \$\_OAI4\_} cells. \end{fixme} diff --git a/manual/CHAPTER_Prog.tex b/manual/CHAPTER_Prog.tex index 3918594a..3cbc95a1 100644 --- a/manual/CHAPTER_Prog.tex +++ b/manual/CHAPTER_Prog.tex @@ -2,16 +2,21 @@ \chapter{Programming Yosys Extensions} \label{chapter:prog} -\begin{fixme} -This chapter will contain a guided tour to the Yosys APIs and conclude -with an example module. -\end{fixme} +This chapter contains some bits and pieces of information about programming +yosys extensions. Also consult the section on programming in the ``Yosys +Presentation'' (can be downloaded from the Yosys website as PDF) and don't +be afraid to ask questions on the Yosys Subreddit. -\section{Programming with RTLIL} -\section{Internal Utility Libraries} -\section{Loadable Modules} +\section{The ``CodingReadme'' File} + +The following is an excerpt of the {\tt CodingReadme} file from the Yosys source tree. + +\lstinputlisting[title=CodingReadme,rangeprefix=--,rangesuffix=--,includerangemarker=false,linerange=snip-snap,numbers=left,frame=single]{../CodingReadme} + +\section{The ``stubsnets'' Example Module} + +The following is the complete code of the ``stubsnets'' example module. It is included in the Yosys source distribution as {\tt manual/CHAPTER\_Prog/stubnets.cc}. -\section{Example Module} \lstinputlisting[title=stubnets.cc,numbers=left,frame=single,language=C++]{CHAPTER_Prog/stubnets.cc} diff --git a/manual/CHAPTER_Prog/.gitignore b/manual/CHAPTER_Prog/.gitignore new file mode 100644 index 00000000..fa83c321 --- /dev/null +++ b/manual/CHAPTER_Prog/.gitignore @@ -0,0 +1,3 @@ +stubnets.so +stubnets.d +*.log diff --git a/manual/CHAPTER_Prog/stubnets.cc b/manual/CHAPTER_Prog/stubnets.cc index ef4b1245..4849c6a7 100644 --- a/manual/CHAPTER_Prog/stubnets.cc +++ b/manual/CHAPTER_Prog/stubnets.cc @@ -5,15 +5,16 @@ // binary, for any purpose, commercial or non-commercial, and by any // means. -#include "kernel/rtlil.h" -#include "kernel/register.h" +#include "kernel/yosys.h" #include "kernel/sigtools.h" -#include "kernel/log.h" #include #include #include +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + // this function is called for each module in the design static void find_stub_nets(RTLIL::Design *design, RTLIL::Module *module, bool report_bits) { @@ -62,7 +63,7 @@ static void find_stub_nets(RTLIL::Design *design, RTLIL::Module *module, bool re // for each bit (unless it is a constant): // check if it is used at least two times and add to stub_bits otherwise - for (int i = 0; i < SIZE(sig); i++) + for (int i = 0; i < GetSize(sig); i++) if (sig[i].wire != NULL && (bit_usage_count[sig[i]] + usage_offset) < 2) stub_bits.insert(i); @@ -72,7 +73,7 @@ static void find_stub_nets(RTLIL::Design *design, RTLIL::Module *module, bool re // report stub bits and/or stub wires, don't report single bits // if called with report_bits set to false. - if (SIZE(stub_bits) == SIZE(sig)) { + if (GetSize(stub_bits) == GetSize(sig)) { log(" found stub wire: %s\n", RTLIL::id2cstr(wire->name)); } else { if (!report_bits) @@ -126,3 +127,4 @@ struct StubnetsPass : public Pass { } } StubnetsPass; +PRIVATE_NAMESPACE_END diff --git a/manual/PRESENTATION_ExAdv.tex b/manual/PRESENTATION_ExAdv.tex index 471516b4..74350091 100644 --- a/manual/PRESENTATION_ExAdv.tex +++ b/manual/PRESENTATION_ExAdv.tex @@ -790,7 +790,7 @@ Unwrap in {\tt test2}: \hfil\begin{tikzpicture} \node at (0,0) {\includegraphics[width=11cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2d.pdf}}; -\node at (0,-4) {\includegraphics[width=11cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2e.pdf}}; +\node at (0,-4) {\includegraphics[width=8cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2e.pdf}}; \node at (1,-1.7) {\begin{lstlisting}[linewidth=5.5cm, frame=single, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] techmap -map macc_xilinx_unwrap_map.v ;; \end{lstlisting}}; diff --git a/manual/PRESENTATION_ExOth/axis_master.v b/manual/PRESENTATION_ExOth/axis_master.v index 25a1feee..fe9008ad 100644 --- a/manual/PRESENTATION_ExOth/axis_master.v +++ b/manual/PRESENTATION_ExOth/axis_master.v @@ -13,7 +13,7 @@ module axis_master(aclk, aresetn, tvalid, tready, tdata); if (tvalid && tready) tvalid <= 0; if (!tvalid || !tready) begin - // ^- should be not inverted! + // ^- should not be inverted! state = state ^ state << 13; state = state ^ state >> 7; state = state ^ state << 17; diff --git a/manual/PRESENTATION_ExSyn.tex b/manual/PRESENTATION_ExSyn.tex index 80398229..b7d6b8a6 100644 --- a/manual/PRESENTATION_ExSyn.tex +++ b/manual/PRESENTATION_ExSyn.tex @@ -268,7 +268,7 @@ memory -nomap; techmap -map my_memory_map.v; memory_map \end{frame} \begin{frame}[t, fragile]{\subsecname{} -- Example 1/2} -\vbox to 0cm{\includegraphics[width=\linewidth,trim=0cm 0cm 0cm -10cm]{PRESENTATION_ExSyn/memory_01.pdf}\vss} +\vbox to 0cm{\includegraphics[width=0.7\linewidth,trim=0cm 0cm 0cm -10cm]{PRESENTATION_ExSyn/memory_01.pdf}\vss} \vskip-1cm \begin{columns} \column[t]{5cm} @@ -455,7 +455,7 @@ read_verilog -D WITH_MULT cpu_alu.v hierarchy -check -top cpu_top # high-level synthesis -proc; opt; memory -nomap;; fsm; opt +proc; opt; fsm;; memory -nomap; opt # substitute block rams techmap -map map_rams.v @@ -497,7 +497,7 @@ the next part (Section 3, ``Advanced Synthesis'') of this presentation.} \item Yosys provides commands for each phase of the synthesis. \item Each command solves a (more or less) simple problem. \item Complex commands are often only front-ends to simple commands. -\item {\tt proc; opt; memory; opt; fsm; opt; techmap; opt; abc;;} +\item {\tt proc; opt; fsm; opt; memory; opt; techmap; opt; abc;;} \end{itemize} \bigskip diff --git a/manual/PRESENTATION_Intro.tex b/manual/PRESENTATION_Intro.tex index 7697266d..5aeebd9f 100644 --- a/manual/PRESENTATION_Intro.tex +++ b/manual/PRESENTATION_Intro.tex @@ -277,7 +277,7 @@ Direct link to the files: \\ \footnotesize \medskip {\color{YosysGreen}\# the high-level stuff}\\ -\boxalert<3>{proc}; \boxalert<4>{opt}; \boxalert<5>{memory}; \boxalert<6>{opt}; \boxalert<7>{fsm}; \boxalert<8>{opt} +\boxalert<3>{proc}; \boxalert<4>{opt}; \boxalert<5>{fsm}; \boxalert<6>{opt}; \boxalert<7>{memory}; \boxalert<8>{opt} \medskip {\color{YosysGreen}\# mapping to internal cell library}\\ @@ -308,9 +308,9 @@ Direct link to the files: \\ \footnotesize \only<2>{hierarchy -check -top counter}% \only<3>{proc}% \only<4>{opt}% -\only<5>{memory}% +\only<5>{fsm}% \only<6>{opt}% -\only<7>{fsm}% +\only<7>{memory}% \only<8>{opt}% \only<9>{techmap}% \only<10>{opt}% @@ -333,13 +333,13 @@ Direct link to the files: \\ \footnotesize Perform some basic optimizations and cleanups. }% \only<5>{ - Analyze memories and create circuits to implement them. + Analyze and optimize finite state machines. }% \only<6>{ Perform some basic optimizations and cleanups. }% \only<7>{ - Analyze and optimize finite state machines. + Analyze memories and create circuits to implement them. }% \only<8>{ Perform some basic optimizations and cleanups. @@ -398,7 +398,7 @@ hierarchy -check -top counter \begin{frame}[t, fragile]{\subsecname{} -- Step 2/4} \begin{verbatim} -proc; opt; memory; opt; fsm; opt +proc; opt; fsm; opt; memory; opt \end{verbatim} \vfill @@ -411,7 +411,7 @@ techmap; opt \end{verbatim} \vfill -\includegraphics[width=\linewidth,trim=0 0cm 0 0cm]{PRESENTATION_Intro/counter_02.pdf} +\includegraphics[width=\linewidth,trim=0 0cm 0 2cm]{PRESENTATION_Intro/counter_02.pdf} \end{frame} \begin{frame}[t, fragile]{\subsecname{} -- Step 4/4} @@ -427,6 +427,48 @@ clean %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\subsection{The synth command} + +\begin{frame}[fragile]{\subsecname{}} +Yosys contains a default (recommended example) synthesis script in form of the +{\tt synth} command. The following commands are executed by this synthesis command: + +\begin{columns} +\column[t]{5cm} +\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] +begin: + hierarchy -check [-top ] + +coarse: + proc + opt + wreduce + alumacc + share + opt + fsm + opt -fast + memory -nomap + opt_clean +\end{lstlisting} +\column[t]{5cm} +\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] +fine: + opt -fast -full + memory_map + opt -full + techmap + opt -fast + +abc: + abc -fast + opt -fast +\end{lstlisting} +\end{columns} +\end{frame} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + \subsection{Yosys Commands} \begin{frame}[fragile]{\subsecname{} 1/3 \hspace{0pt plus 1 filll} (excerpt)} @@ -500,6 +542,7 @@ Commands for writing the results: \bigskip Script-Commands for standard synthesis tasks: \begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] + synth # generic synthesis script synth_xilinx # synthesis for Xilinx FPGAs \end{lstlisting} @@ -603,12 +646,8 @@ endmodule \begin{frame}{\subsecname} \begin{itemize} -\item Multi-dimensional arrays (memories) -\item Writing to arrays using bit- and part-selects (todo for 0.4.0) -\item The wor/wand wire types (maybe for 0.4.0) \item Tri-state logic - -\bigskip +\item The wor/wand wire types (maybe for 0.5) \item Latched logic (is synthesized as logic with feedback loops) \item Some non-synthesizable features that should be ignored in synthesis are not supported by the parser and cause a parser error (file a bug report if you encounter this problem) \end{itemize} @@ -813,10 +852,12 @@ control logic because it is simpler than setting up a commercial flow. \item When building on other Linux distributions: \begin{itemize} \item Needs compiler with some C++11 support +\item See README file for build instructions \item Post to the subreddit if you get stuck \end{itemize} \item Ported to OS X (Darwin) and OpenBSD -\item No win32 support (yet) +\item Native win32 build with VisualStudio +\item Cross win32 build with MXE \end{itemize} \end{frame} diff --git a/manual/PRESENTATION_Prog.tex b/manual/PRESENTATION_Prog.tex index 590451be..96189e55 100644 --- a/manual/PRESENTATION_Prog.tex +++ b/manual/PRESENTATION_Prog.tex @@ -89,12 +89,13 @@ left with a much simpler version of RTLIL: \bigskip Many commands simply choose to only work on this simpler version: \begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont] -if (module->processes.size() != 0 || module->memories.size() != 0) - log_error("This command does not operate on modules with processes " - "and/or memories! Run 'proc' and 'memory' first.\n"); +for (RTLIL::Module *module : design->selected_modules() { + if (module->has_memories_warn() || module->has_processes_warn()) + continue; + .... +} \end{lstlisting} -\bigskip For simplicity we only discuss this version of RTLIL in this presentation. \end{frame} @@ -122,8 +123,9 @@ has been executed. \subsection{The RTLIL Data Structures} \begin{frame}{\subsecname} -The RTLIL data structures are simple structs utilizing C++ {\tt std::} -containers. +The RTLIL data structures are simple structs utilizing {\tt pool<>} and +{\tt dict<>} containers (drop-in replacementments for {\tt +std::unordered\_set<>} and {\tt std::unordered\_map<>}). \bigskip \begin{itemize} @@ -145,7 +147,9 @@ See {\tt yosys/kernel/rtlil.h} for details. \subsubsection{RTLIL::IdString} \begin{frame}{\subsubsecname}{} -{\tt RTLIL::IdString} is a simple wrapper for {\tt std::string}. It is used for names of RTLIL objects. +{\tt RTLIL::IdString} in many ways behave like a {\tt std::string}. It is used +for names of RTLIL objects. Internally a RTLIL::IdString object is only a +single integer. \medskip The first character of a {\tt RTLIL::IdString} specifies if the name is {\it public\/} or {\it private\/}: @@ -168,25 +172,25 @@ Use the {\tt NEW\_ID} macro to create a new unique private name. \begin{frame}[t, fragile]{\subsubsecname} The {\tt RTLIL::Design} and {\tt RTLIL::Module} structs are the top-level RTLIL -data structures. - -Yosys always operates on one active design, but can hold many designs in memory. +data structures. Yosys always operates on one active design, but can hold many designs in memory. \bigskip \begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] struct RTLIL::Design { - std::map modules; + dict modules_; ... }; struct RTLIL::Module { RTLIL::IdString name; - std::map wires; - std::map cells; - std::vector connections; + dict wires_; + dict cells_; + std::vector connections_; ... }; \end{lstlisting} + +(Use the various accessor functions instead of directly working with the {\tt *\_} members.) \end{frame} \subsubsection{The RTLIL::Wire Structure} @@ -251,21 +255,22 @@ constants are part of the RTLIL representation itself. \begin{frame}[t, fragile]{\subsubsecname} The {\tt RTLIL::SigSpec} struct represents a signal vector. Each bit can either be a bit from a wire -or a constant value. Consecutive bits from a wire or consecutive constant bits are consolidated into -a {\tt RTLIL::SigChunk}: +or a constant value. \bigskip \begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -struct RTLIL::SigChunk { +struct RTLIL::SigBit +{ RTLIL::Wire *wire; - RTLIL::Const data; // only used if wire == NULL - int width, offset; + union { + RTLIL::State data; // used if wire == NULL + int offset; // used if wire != NULL + }; ... }; struct RTLIL::SigSpec { - std::vector chunks; // LSB at index 0 - int width; + std::vector bits_; // LSB at index 0 ... }; \end{lstlisting} @@ -289,8 +294,8 @@ instances: \begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] struct RTLIL::Cell { RTLIL::IdString name, type; - std::map connections; - std::map parameters; + dict connections_; + dict parameters; ... }; \end{lstlisting} @@ -345,7 +350,7 @@ typedef std::pair RTLIL::SigSig; struct RTLIL::Module { ... - std::vector connections; + std::vector connections_; ... }; \end{lstlisting} @@ -354,8 +359,8 @@ struct RTLIL::Module { {\tt RTLIL::SigSig::first} is the driven signal and {\tt RTLIL::SigSig::second} is the driving signal. Example usage (setting wire {\tt foo} to value {\tt 42}): \begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -module->connections.push_back(RTLIL::SigSig(module->wires.at("\\foo"), - RTLIL::SigSpec(42, module->wires.at("\\foo")->width))); +module->connect(module->wire("\\foo"), + RTLIL::SigSpec(42, module->wire("\\foo")->width)); \end{lstlisting} \end{frame} @@ -378,17 +383,19 @@ endmodule RTLIL::Module *module = new RTLIL::Module; module->name = "\\absval"; -RTLIL::Wire *a = module->new_wire(4, "\\a"); +RTLIL::Wire *a = module->addWire("\\a", 4); a->port_input = true; a->port_id = 1; -RTLIL::Wire *y = module->new_wire(4, "\\y"); +RTLIL::Wire *y = module->addWire("\\y", 4); y->port_output = true; y->port_id = 2; -RTLIL::Wire *a_inv = module->new_wire(4, NEW_ID); +RTLIL::Wire *a_inv = module->addWire(NEW_ID, 4); module->addNeg(NEW_ID, a, a_inv, true); module->addMux(NEW_ID, a, a_inv, RTLIL::SigSpec(a, 1, 3), y); + +module->fixup_ports(); \end{lstlisting} \end{frame} @@ -431,8 +438,8 @@ In this case {\tt a}, {\tt x}, and {\tt y} are all different names for the same \smallskip \begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -RTLIL::SigSpec a(module->wires.at("\\a")), x(module->wires.at("\\x")), - y(module->wires.at("\\y")); +RTLIL::SigSpec a(module->wire("\\a")), x(module->wire("\\x")), + y(module->wire("\\y")); log("%d %d %d\n", a == x, x == y, y == a); // will print "0 0 0" \end{lstlisting} @@ -462,9 +469,9 @@ log("Mapped signal x: %s\n", log_signal(sigmap(x))); \end{lstlisting} \medskip -Use {\tt RTLIL::id2cstr()} to create a C-string for an {\tt RTLIL::IdString}: +Use {\tt log\_id()} to create a C-string for an {\tt RTLIL::IdString}: \begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -log("Name of this module: %s\n", RTLIL::id2cstr(module->name)); +log("Name of this module: %s\n", log_id(module->name)); \end{lstlisting} \medskip @@ -513,9 +520,8 @@ a new yosys command: \bigskip \begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -#include "kernel/rtlil.h" -#include "kernel/register.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +USING_YOSYS_NAMESPACE struct MyPass : public Pass { MyPass() : Pass("my_cmd", "just a simple test") { } @@ -526,9 +532,9 @@ struct MyPass : public Pass { log(" %s\n", arg.c_str()); log("Modules in current design:\n"); - for (auto &mod : design->modules) - log(" %s (%zd wires, %zd cells)\n", RTLIL::id2cstr(mod.first), - mod.second->wires.size(), mod.second->cells.size()); + for (auto mod : design->modules()) + log(" %s (%d wires, %d cells)\n", log_id(mod), + GetSize(mod->wires), GetSize(mod->cells)); } } MyPass; \end{lstlisting} @@ -549,6 +555,12 @@ yosys-config --exec --cxx --cxxflags --ldflags \ -o my_cmd.so -shared my_cmd.cc --ldlibs \end{lstlisting} +\bigskip +Or shorter: +\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont] +yosys-config --build my_cmd.so my_cmd.cc +\end{lstlisting} + \bigskip Load the plugin using the yosys {\tt -m} option: \begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont] @@ -566,7 +578,7 @@ yosys -m ./my_cmd.so -p 'my_cmd foo bar' \item \dots and even simpler if you don't need RTLIL::Memory or RTLIL::Process objects. \bigskip -\item Writing synthesis software? Consider learning the Yosys API and make your stuff +\item Writing synthesis software? Consider learning the Yosys API and make your work part of the Yosys framework. \end{itemize} diff --git a/manual/PRESENTATION_Prog/.gitignore b/manual/PRESENTATION_Prog/.gitignore index 7fd56076..ccdd6bd5 100644 --- a/manual/PRESENTATION_Prog/.gitignore +++ b/manual/PRESENTATION_Prog/.gitignore @@ -1 +1,2 @@ my_cmd.so +my_cmd.d diff --git a/manual/PRESENTATION_Prog/my_cmd.cc b/manual/PRESENTATION_Prog/my_cmd.cc index 381b0587..1d28ce97 100644 --- a/manual/PRESENTATION_Prog/my_cmd.cc +++ b/manual/PRESENTATION_Prog/my_cmd.cc @@ -1,6 +1,9 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + struct MyPass : public Pass { MyPass() : Pass("my_cmd", "just a simple test") { } virtual void execute(std::vector args, RTLIL::Design *design) @@ -12,7 +15,7 @@ struct MyPass : public Pass { log("Modules in current design:\n"); for (auto mod : design->modules()) log(" %s (%zd wires, %zd cells)\n", log_id(mod), - SIZE(mod->wires()), SIZE(mod->cells())); + GetSize(mod->wires()), GetSize(mod->cells())); } } MyPass; @@ -25,6 +28,7 @@ struct Test1Pass : public Pass { log_error("A module with the name absval already exists!\n"); RTLIL::Module *module = design->addModule("\\absval"); + log("Name of this module: %s\n", log_id(module)); RTLIL::Wire *a = module->addWire("\\a", 4); a->port_input = true; @@ -38,7 +42,7 @@ struct Test1Pass : public Pass { module->addNeg(NEW_ID, a, a_inv, true); module->addMux(NEW_ID, a, a_inv, RTLIL::SigSpec(a, 3), y); - log("Name of this module: %s\n", log_id(module)); + module->fixup_ports(); } } Test1Pass; @@ -69,3 +73,4 @@ struct Test2Pass : public Pass { } } Test2Pass; +PRIVATE_NAMESPACE_END diff --git a/manual/command-reference-manual.tex b/manual/command-reference-manual.tex index 35249ed8..d653f409 100644 --- a/manual/command-reference-manual.tex +++ b/manual/command-reference-manual.tex @@ -15,22 +15,99 @@ library to a target architecture. -script use the specified ABC script file instead of the default script. + if starts with a plus sign (+), then the rest of the filename + string is interprated 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. + + if no -script parameter is given, the following scripts are used: + + for -liberty without -constr: + strash; scorr; ifraig; retime {D}; strash; dch -f; map {D} + + for -liberty with -constr: + strash; scorr; ifraig; retime {D}; strash; dch -f; map {D}; + buffer; upsize {D}; dnsize {D}; stime -p + + for -lut: + strash; scorr; ifraig; retime; strash; dch -f; if + + otherwise: + strash; scorr; ifraig; retime; strash; dch -f; map + + -fast + use different default scripts that are slightly faster (at the cost + of output quality): + + for -liberty without -constr: + retime {D}; map {D} + + for -liberty with -constr: + retime {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p + + for -lut: + retime; if + + otherwise: + retime; map + -liberty generate netlists for the specified cell library (using the liberty - file format). Without this option, ABC is used to optimize the netlist - but keeps using yosys's internal gate library. This option is ignored if - the -script option is also used. + file format). -constr - pass this file with timing constraints to ABC + pass this file with timing constraints to ABC. use with -liberty. + + a constr file contains two lines: + set_driving_cell + set_load + + the set_driving_cell statement defines which cell type is assumed to + drive the primary inputs and the set_load statement sets the load in + femtofarads for each primary output. + + -D + set delay target. the string {D} in the default scripts above is + replaced by this option when used, and an empty string otherwise. -lut generate netlist using luts of (max) the specified width. + -lut : + generate netlist using luts of (max) the specified width . All + luts with width <= have constant cost. for luts larger than + the area cost doubles with each additional input bit. the delay cost + is still constant for all lut widths. + + -dff + also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many + clock domains are automatically partitioned in clock domains and each + domain is passed through ABC independently. + + -clk [!][,[!]] + use only the specified clock domain. this is like -dff, but only FF + cells that belong to the specified clock domain are used. + + -keepff + set the "keep" attribute on flip-flop output wires. (and thus preserve + them, for example for equivialence checking.) + -nocleanup when this option is used, the temporary files created by this pass are not removed. this is useful for debugging. + -showtmp + print the temp dir name in log. usually this is suppressed so that the + command output is identical across runs. + + -markgroups + set a 'abcgroup' attribute on all objects created by ABC. The value of + this attribute is a unique integer for each ABC process started. This + is useful for debugging the partitioning of clock domains. + +When neither -liberty nor -lut is used, the Yosys standard cell library is +loaded into ABC before the ABC script is executed. + 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.) @@ -59,6 +136,15 @@ Like 'add -input', but also connect the signal between instances of the selected modules. \end{lstlisting} +\section{alumacc -- extract ALU and MACC cells} +\label{cmd:alumacc} +\begin{lstlisting}[numbers=left,frame=single] + alumacc [selection] + +This pass translates arithmetic operations like $add, $mul, $lt, etc. to $alu +and $macc cells. +\end{lstlisting} + \section{cd -- a shortcut for 'select -module '} \label{cmd:cd} \begin{lstlisting}[numbers=left,frame=single] @@ -92,6 +178,129 @@ When commands are separated using the ';;;' token, this command will be executed in -purge mode between the commands. \end{lstlisting} +\section{connect -- create or remove connections} +\label{cmd:connect} +\begin{lstlisting}[numbers=left,frame=single] + connect [-nomap] [-nounset] -set + +Create a connection. This is equivialent to adding the statement 'assign + = ;' to the verilog input. Per default, all existing +drivers for are unconnected. This can be overwritten by using +the -nounset option. + + + connect [-nomap] -unset + +Unconnect all existing drivers for the specified expression. + + + connect [-nomap] -port + +Connect the specified cell port to the specified cell port. + + +Per default signal alias names are resolved and all signal names are mapped +the the signal name of the primary driver. Using the -nomap option deactivates +this behavior. + +The connect command operates in one module only. Either only one module must +be selected or an active module must be set using the 'cd' command. + +This command does not operate on module with processes. +\end{lstlisting} + +\section{connwrappers -- replace undef values with defined constants} +\label{cmd:connwrappers} +\begin{lstlisting}[numbers=left,frame=single] + connwrappers [options] [selection] + +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 +the driving cell. + + -signed + -unsigned + consider the specified signed/unsigned wrapper output + + -port + use the specified parameter to decide if signed or unsigned + +The options -signed, -unsigned, and -port can be specified multiple times. +\end{lstlisting} + +\section{copy -- copy modules in the design} +\label{cmd:copy} +\begin{lstlisting}[numbers=left,frame=single] + copy old_name new_name + +Copy the specified module. Note that selection patterns are not supported +by this command. +\end{lstlisting} + +\section{cover -- print code coverage counters} +\label{cmd:cover} +\begin{lstlisting}[numbers=left,frame=single] + cover [options] [pattern] + +Print the code coverage counters collected using the cover() macro in the Yosys +C++ code. This is useful to figure out what parts of Yosys are utilized by a +test bench. + + -q + Do not print output to the normal destination (console and/or log file) + + -o file + Write output to this file, truncate if exists. + + -a file + Write output to this file, append if exists. + + -d dir + Write output to a newly created file in the specified directory. + +When one or more pattern (shell wildcards) are specified, then only counters +matching at least one pattern are printed. + + +It is also possible to instruct Yosys to print the coverage counters on program +exit to a file using environment variables: + + YOSYS_COVER_DIR="{dir-name}" yosys {args} + + This will create a file (with an auto-generated name) in this + directory and write the coverage counters to it. + + YOSYS_COVER_FILE="{file-name}" yosys {args} + + This will append the coverage counters to the specified file. + + +Hint: Use the following AWK command to consolidate Yosys coverage files: + + gawk '{ p[$3] = $1; c[$3] += $2; } END { for (i in p) + printf "%-60s %10d %s\n", p[i], c[i], i; }' {files} | sort -k3 + + +Coverage counters are only available in Yosys for Linux. +\end{lstlisting} + +\section{delete -- delete objects in the design} +\label{cmd:delete} +\begin{lstlisting}[numbers=left,frame=single] + delete [selection] + +Deletes the selected objects. This will also remove entire modules, if the +whole module is selected. + + + delete {-input|-output|-port} [selection] + +Does not delete any object but removes the input and/or output flag on the +selected wires, thus 'deleting' module ports. +\end{lstlisting} + \section{design -- save, restore and reset current design} \label{cmd:design} \begin{lstlisting}[numbers=left,frame=single] @@ -105,22 +314,75 @@ Clear the current design. Save the current design under the given name. + design -stash + +Save the current design under the given name and then clear the current design. + + + design -push + +Push the current design to the stack and then clear the current design. + + + design -pop + +Reset the current design and pop the last design from the stack. + + design -load Reset the current design and load the design previously saved under the given name. + + + design -copy-from [-as ] + +Copy modules from the specified design into the current one. The selection is +evaluated in the other design. + + + design -copy-to [-as ] [selection] + +Copy modules from the current design into the soecified one. +\end{lstlisting} + +\section{dff2dffe -- transform \$dff cells to \$dffe cells} +\label{cmd:dff2dffe} +\begin{lstlisting}[numbers=left,frame=single] + dff2dffe [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 +$_DFF_P_, $_DFF_N_ and $_MUX_. + + -unmap + operate in the opposite direction: replace $dffe cells with combinations + of $dff and $mux cells. the options below are ignore in unmap mode. + + -direct + map directly to external gate type. can + be any internal gate-level FF cell (except $_DFFE_??_). the + is the cell type name for a cell with an + identical interface to the , except it + also has an high-active enable port 'E'. + Usually is an intemediate cell type + that is then translated to the final type using 'techmap'. \end{lstlisting} \section{dfflibmap -- technology mapping of flip-flops} \label{cmd:dfflibmap} \begin{lstlisting}[numbers=left,frame=single] - dfflibmap -liberty [selection] + dfflibmap [-prepare] -liberty [selection] Map internal flip-flop cells to the flip-flop cells in the technology library specified in the given liberty file. This pass may add inverters as needed. Therefore it is recommended to first run this pass and then map the logic paths to the target technology. + +When called with -prepare, this command will convert the internal FF cells +to the internal cell types that best match the cells found in the given +liberty file. \end{lstlisting} \section{dump -- print parts of the design in ilang format} @@ -138,8 +400,151 @@ ilang format. -n only dump the module headers if the entire module is selected - -outfile - Write to the specified file. + -o + write to the specified file. + + -a + like -outfile but append instead of overwrite +\end{lstlisting} + +\section{echo -- turning echoing back of commands on and off} +\label{cmd:echo} +\begin{lstlisting}[numbers=left,frame=single] + echo on + +Print all commands to log before executing them. + + + echo off + +Do not print all commands to log before executing them. (default) +\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 + +This command adds an $equiv cell for the specified signals. +\end{lstlisting} + +\section{equiv\_induct -- proving \$equiv cells using temporal induction} +\label{cmd:equiv_induct} +\begin{lstlisting}[numbers=left,frame=single] + equiv_induct [options] [selection] + +Uses a version of temporal induction to prove $equiv cells. + +Only selected $equiv cells are proven and only selected cells are used to +perform the proof. + + -undef + enable modelling of undef states + + -seq + the max. number of time steps to be considered (default = 4) + +This command is very effective in proving complex sequential circuits, when +the internal state of the circuit quickly propagates to $equiv cells. + +However, this command uses a weak definition of 'equivalence': This command +proves that the two circuits will not diverge after they produce equal +outputs (observable points via $equiv) for at least cycles (the +specified via -seq). + +Combined with simulation this is very powerful because simulation can give +you confidence that the circuits start out synced for at least cycles +after reset. +\end{lstlisting} + +\section{equiv\_make -- prepare a circuit for equivalence checking} +\label{cmd:equiv_make} +\begin{lstlisting}[numbers=left,frame=single] + equiv_make [options] gold_module gate_module equiv_module + +This creates a module annotated with $equiv cells from two presumably +equivalent modules. Use commands such as 'equiv_simple' and 'equiv_status' +to work with the created equivalent checking module. + + -inames + Also match cells and wires with $... names. + + -blacklist + Do not match cells or signals that match the names in the file. + + -encfile + Match FSM encodings using the desiption from the file. + See 'help fsm_recode' for details. + +Note: The circuit created by this command is not a miter (with something like +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\_miter -- extract miter from equiv circuit} +\label{cmd:equiv_miter} +\begin{lstlisting}[numbers=left,frame=single] + equiv_miter [options] miter_module [selection] + +This creates a miter module for further analysis of the selected $equiv cells. + + -trigger + Create a trigger output + + -cmp + Create cmp_* outputs for individual unproven $equiv cells + + -assert + Create a $assert cell for each unproven $equiv cell + + -undef + Create compare logic that handles undefs correctly +\end{lstlisting} + +\section{equiv\_remove -- remove \$equiv cells} +\label{cmd:equiv_remove} +\begin{lstlisting}[numbers=left,frame=single] + equiv_remove [options] [selection] + +This command removes the selected $equiv cells. If neither -gold nor -gate is +used then only proven cells are removed. + + -gold + keep gold circuit + + -gate + keep gate circuit +\end{lstlisting} + +\section{equiv\_simple -- try proving simple \$equiv instances} +\label{cmd:equiv_simple} +\begin{lstlisting}[numbers=left,frame=single] + equiv_simple [options] [selection] + +This command tries to prove $equiv cells using a simple direct SAT approach. + + -v + verbose output + + -undef + enable modelling of undef states + + -nogroup + disabling grouping of $equiv cells by output wire + + -seq + the max. number of time steps to be considered (default = 1) +\end{lstlisting} + +\section{equiv\_status -- print status of equivalent checking module} +\label{cmd:equiv_status} +\begin{lstlisting}[numbers=left,frame=single] + equiv_status [options] [selection] + +This command prints status information for all selected $equiv cells. + + -assert + produce an error if any unproven $equiv cell is found \end{lstlisting} \section{eval -- evaluate the circuit given an input} @@ -164,6 +569,38 @@ inputs. then all output ports of the current module are used. \end{lstlisting} +\section{expose -- convert internal signals to module ports} +\label{cmd:expose} +\begin{lstlisting}[numbers=left,frame=single] + expose [options] [selection] + +This command exposes all selected internal signals of a module as additional +outputs. + + -dff + only consider wires that are directly driven by register cell. + + -cut + when exposing a wire, create an input/output pair and cut the internal + 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. + + -evert + also turn connections to instances of other modules to additional + inputs and outputs and remove the module instances. + + -evert-dff + turn flip-flops to sets of inputs and outputs. + + -sep + when creating new wire/port names, the original object name is suffixed + with this separator (default: '.') and the port name or a type + designator for the exposed signal. +\end{lstlisting} + \section{extract -- find subcircuits and replace them with cells} \label{cmd:extract} \begin{lstlisting}[numbers=left,frame=single] @@ -175,7 +612,12 @@ 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 - use the modules in this file as reference + use the modules in this file as reference. This option can be used + multiple times. + + -map % + use the modules in this in-memory design as reference. This option can + be used multiple times. -verbose print debug output while analyzing @@ -209,6 +651,12 @@ map file can be a verilog source file (*.v) or an ilang file (*.il). -wire_attr Attributes on wires with the given name must match. + -ignore_parameters + Do not use parameters when matching cells. + + -ignore_param + Do not use this parameter when matching cells. + This pass does not operate on modules with uprocessed processes in it. (I.e. the 'proc' pass should be used first to convert processes to netlists.) @@ -257,11 +705,27 @@ pass is using the current design as mapping library. 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 -removed. +disconnected. A subsequent call to 'clean' will remove the redundant drivers. - -try - do not issue an error when the analysis fails. - (usually beacause of logic loops in the design) + -v, -vv + enable verbose or very verbose output + + -inv + enable explicit handling of inverted signals + + -stop + stop after reduction operations. this is mostly used for + debugging the freduce command itself. + + -dump + dump the design to __.il after each reduction + 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. + +All selected wires are considered for rewiring. The selected cells cover the +circuit that is analyzed. \end{lstlisting} \section{fsm -- extract and optimize finite state machines} @@ -297,6 +761,7 @@ Options: -encoding tye -fm_set_fsm_file file + -encfile file passed through to fsm_recode pass \end{lstlisting} @@ -393,16 +858,24 @@ combination with the 'opt_clean' pass (see also 'help fsm'). \section{fsm\_recode -- recoding finite state machines} \label{cmd:fsm_recode} \begin{lstlisting}[numbers=left,frame=single] - fsm_recode [-encoding type] [-fm_set_fsm_file file] [selection] + fsm_recode [options] [selection] This pass reassign the state encodings for FSM cells. At the moment only -one-hot encoding and binary encoding is supported. The option -encoding -can be used to specify the encoding scheme used for FSMs without the -`fsm_encoding' attribute (or with the attribute set to `auto'. +one-hot encoding and binary encoding is supported. + -encoding + specify the encoding scheme used for FSMs without the + 'fsm_encoding' attribute or with the attribute set to `auto'. -The option -fm_set_fsm_file can be used to generate a file containing the -mapping from old to new FSM encoding in form of Synopsys Formality set_fsm_* -commands. + -fm_set_fsm_file + generate a file containing the mapping from old to new FSM encoding + in form of Synopsys Formality set_fsm_* commands. + + -encfile + write the mappings from old to new FSM encoding to a file in the + following format: + + .fsm + .map \end{lstlisting} \section{help -- display help messages} @@ -428,10 +901,24 @@ needed. also check the design hierarchy. this generates an error when an unknown module is used as cell type. + -purge_lib + by default the hierarchy command will not remove library (blackbox) + module. use this options to also remove unused blackbox modules. + + -libdir + search for files named .v in the specified directory + for unknown modules and automatically run read_verilog for each + unknown module. + -keep_positionals per default this pass also converts positional arguments in cells to arguments using port names. this option disables this behavior. + -nokeep_asserts + per default this pass sets the "keep" attribute on all modules + that directly or indirectly contain one or more $assert cells. this + option disables this behavior. + -top use the specified top module to built a design hierarchy. modules outside this tree (unused modules) are removed. @@ -457,6 +944,25 @@ This pass ignores the current selection and always operates on all modules in the current design. \end{lstlisting} +\section{hilomap -- technology mapping of constant hi- and/or lo-drivers} +\label{cmd:hilomap} +\begin{lstlisting}[numbers=left,frame=single] + hilomap [options] [selection] + +Map constants to 'tielo' and 'tiehi' driver cells. + + -hicell + Replace constant hi bits with this cell. + + -locell + Replace constant lo bits with this cell. + + -singleton + Create only one hi/lo cell and connect all constant bits + to that cell. Per default a separate cell is created for + each constant bit. +\end{lstlisting} + \section{history -- show last interactive commands} \label{cmd:history} \begin{lstlisting}[numbers=left,frame=single] @@ -491,39 +997,160 @@ the resulting cells to more sophisticated PAD cells. -nameparam Use the specified parameter to set the port name. + + -bits + create individual bit-wide buffers even for ports that + are wider. (the default behavior is to create word-wide + buffers using -widthparam to set the word size on the cell.) +\end{lstlisting} + +\section{log -- print text and log files} +\label{cmd:log} +\begin{lstlisting}[numbers=left,frame=single] + log string + +Print the given string to the screen and/or the log file. This is useful for TCL +scripts, because the TCL command "puts" only goes to stdout but not to +logfiles. + + -stdout + Print the output to stdout too. This is useful when all Yosys is executed + with a script and the -q (quiet operation) argument to notify the user. + + -stderr + Print the output to stderr too. + + -nolog + Don't use the internal log() command. Use either -stdout or -stderr, + otherwise no output will be generated at all. + + -n + do not append a newline \end{lstlisting} \section{ls -- list modules or objects in modules} \label{cmd:ls} \begin{lstlisting}[numbers=left,frame=single] - ls [pattern] + ls [selection] -When no active module is selected, this prints a list of all modules. +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} -If a pattern is given, the objects matching the pattern are printed +\section{maccmap -- mapping macc cells} +\label{cmd:maccmap} +\begin{lstlisting}[numbers=left,frame=single] + maccmap [-unmap] [selection] -Note that this command does not use the selection mechanism and always operates -on the whole design or whole active module. Use 'select -list' to show a list -of currently selected objects. +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. \end{lstlisting} \section{memory -- translate memories to basic cells} \label{cmd:memory} \begin{lstlisting}[numbers=left,frame=single] - memory [-nomap] [selection] + memory [-nomap] [-bram ] [selection] This pass calls all the other memory_* passes in a useful order: memory_dff + opt_clean + memory_share + opt_clean memory_collect - memory_map (skipped if called with -nomap) + memory_bram -rules (when called with -bram) + memory_map (skipped if called with -nomap) This converts memories to word-wide DFFs and address decoders or multiport memory blocks if called with the -nomap option. \end{lstlisting} +\section{memory\_bram -- map memories to block rams} +\label{cmd:memory_bram} +\begin{lstlisting}[numbers=left,frame=single] + memory_bram -rules [selection] + +This pass converts the multi-port $mem memory cells into block ram instances. +The given rules file describes the available resources and how they should be +used. + +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 + 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) + clocks 1 2 # clock configuration + clkpol 2 2 # clock polarity configuration + endbram + +For the option 'transp' the value 0 means non-transparent, 1 means transparent +and a value greater than 1 means configurable. All groups with the same +value greater than 1 share the same configuration bit. + +For the option 'clocks' the value 0 means non-clocked, and a value greater +than 0 means clocked. All groups with the same value share the same clock +signal. + +For the option 'clkpol' the value 0 means negative edge, 1 means positive edge +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. + +It is also possible to create variants by repeating statements in the bram block +and appending '@