Merge branch 'multimode_clb' of https://github.com/LNIS-Projects/OpenFPGA into multimode_clb
This commit is contained in:
commit
ee1a24d4ba
|
@ -0,0 +1,8 @@
|
|||
brew "bison"
|
||||
brew "flex"
|
||||
brew "gawk"
|
||||
brew "libffi"
|
||||
brew "git"
|
||||
brew "graphviz"
|
||||
brew "pkg-config"
|
||||
brew "python3"
|
136
yosys/CHANGELOG
136
yosys/CHANGELOG
|
@ -3,6 +3,142 @@ List of major changes and improvements between releases
|
|||
=======================================================
|
||||
|
||||
|
||||
Yosys 0.8 .. Yosys 0.8-dev
|
||||
--------------------------
|
||||
|
||||
* Various
|
||||
- Added $changed support to read_verilog
|
||||
- Added "write_edif -attrprop"
|
||||
- Added "ice40_unlut" pass
|
||||
- Added "opt_lut" pass
|
||||
- Added "synth_ice40 -relut"
|
||||
- Added "synth_ice40 -noabc"
|
||||
- Added "gate2lut.v" techmap rule
|
||||
- Added "rename -src"
|
||||
- Added "equiv_opt" pass
|
||||
|
||||
|
||||
Yosys 0.7 .. Yosys 0.8
|
||||
----------------------
|
||||
|
||||
* Various
|
||||
- Many bugfixes and small improvements
|
||||
- Strip debug symbols from installed binary
|
||||
- Replace -ignore_redef with -[no]overwrite in front-ends
|
||||
- Added write_verilog hex dump support, add -nohex option
|
||||
- Added "write_verilog -decimal"
|
||||
- Added "scc -set_attr"
|
||||
- Added "verilog_defines" command
|
||||
- Remeber defines from one read_verilog to next
|
||||
- Added support for hierarchical defparam
|
||||
- Added FIRRTL back-end
|
||||
- Improved ABC default scripts
|
||||
- Added "design -reset-vlog"
|
||||
- Added "yosys -W regex", "yosys -w regex", and "yosys -e regex"
|
||||
- Added Verilog $rtoi and $itor support
|
||||
- Added "check -initdrv"
|
||||
- Added "read_blif -wideports"
|
||||
- Added support for systemVerilog "++" and "--" operators
|
||||
- Added support for SystemVerilog unique, unique0, and priority case
|
||||
- Added "write_edif" options for edif "flavors"
|
||||
- Added support for resetall compiler directive
|
||||
- Added simple C beck-end (bitwise combinatorical only atm)
|
||||
- Added $_ANDNOT_ and $_ORNOT_ cell types
|
||||
- Added cell library aliases to "abc -g"
|
||||
- Added "setundef -anyseq"
|
||||
- Added "chtype" command
|
||||
- Added "design -import"
|
||||
- Added "write_table" command
|
||||
- Added "read_json" command
|
||||
- Added "sim" command
|
||||
- Added "extract_fa" and "extract_reduce" commands
|
||||
- Added "extract_counter" command
|
||||
- Added "opt_demorgan" command
|
||||
- Added support for $size and $bits SystemVerilog functions
|
||||
- Added "blackbox" command
|
||||
- Added "ltp" command
|
||||
- Added support for editline as replacement for readline
|
||||
- Added warnings for driver-driver conflicts between FFs (and other cells) and constants
|
||||
- Added "yosys -E" for creating Makefile dependencies files
|
||||
- Added "synth -noshare"
|
||||
- Added "memory_nordff"
|
||||
- Added "setundef -undef -expose -anyconst"
|
||||
- Added "expose -input"
|
||||
- Added specify/specparam parser support (simply ignore them)
|
||||
- Added "write_blif -inames -iattr"
|
||||
- Added "hierarchy -simcheck"
|
||||
- Added an option to statically link abc into yosys
|
||||
- Added protobuf back-end
|
||||
- Added BLIF parsing support for .conn and .cname
|
||||
- Added read_verilog error checking for reg/wire/logic misuse
|
||||
- Added "make coverage" and ENABLE_GCOV build option
|
||||
|
||||
* Changes in Yosys APIs
|
||||
- Added ConstEval defaultval feature
|
||||
- Added {get,set}_src_attribute() methods on RTLIL::AttrObject
|
||||
- Added SigSpec::is_fully_ones() and Const::is_fully_ones()
|
||||
- Added log_file_warning() and log_file_error() functions
|
||||
|
||||
* Formal Verification
|
||||
- Added "write_aiger"
|
||||
- Added "yosys-smtbmc --aig"
|
||||
- Added "always <positive_int>" to .smtc format
|
||||
- Added $cover cell type and support for cover properties
|
||||
- Added $fair/$live cell type and support for liveness properties
|
||||
- Added smtbmc support for memory vcd dumping
|
||||
- Added "chformal" command
|
||||
- Added "write_smt2 -stbv" and "write_smt2 -stdt"
|
||||
- Fix equiv_simple, old behavior now available with "equiv_simple -short"
|
||||
- Change to Yices2 as default SMT solver (it is GPL now)
|
||||
- Added "yosys-smtbmc --presat" (now default in SymbiYosys)
|
||||
- Added "yosys-smtbmc --smtc-init --smtc-top --noinit"
|
||||
- Added a brand new "write_btor" command for BTOR2
|
||||
- Added clk2fflogic memory support and other improvements
|
||||
- Added "async memory write" support to write_smt2
|
||||
- Simulate clock toggling in yosys-smtbmc VCD output
|
||||
- Added $allseq/$allconst cells for EA-solving
|
||||
- Make -nordff the default in "prep"
|
||||
- Added (* gclk *) attribute
|
||||
- Added "async2sync" pass for single-clock designs with async resets
|
||||
|
||||
* Verific support
|
||||
- Many improvements in Verific front-end
|
||||
- Added proper handling of concurent SVA properties
|
||||
- Map "const" and "rand const" to $anyseq/$anyconst
|
||||
- Added "verific -import -flatten" and "verific -import -extnets"
|
||||
- Added "verific -vlog-incdir -vlog-define -vlog-libdir"
|
||||
- Remove PSL support (because PSL has been removed in upstream Verific)
|
||||
- Improve integration with "hierarchy" command design elaboration
|
||||
- Added YOSYS_NOVERIFIC for running non-verific test cases with verific bin
|
||||
- Added simpilied "read" command that automatically uses verific if available
|
||||
- Added "verific -set-<severity> <msg_id>.."
|
||||
- Added "verific -work <libname>"
|
||||
|
||||
* New back-ends
|
||||
- Added initial Coolrunner-II support
|
||||
- Added initial eASIC support
|
||||
- Added initial ECP5 support
|
||||
|
||||
* GreenPAK Support
|
||||
- Added support for GP_DLATCH, GP_SPI, GP_DCMx, GP_COUNTx, etc.
|
||||
|
||||
* iCE40 Support
|
||||
- Add "synth_ice40 -vpr"
|
||||
- Add "synth_ice40 -nodffe"
|
||||
- Add "synth_ice40 -json"
|
||||
- Add Support for UltraPlus cells
|
||||
|
||||
* MAX10 and Cyclone IV Support
|
||||
- Added initial version of metacommand "synth_intel".
|
||||
- Improved write_verilog command to produce VQM netlist for Quartus Prime.
|
||||
- Added support for MAX10 FPGA family synthesis.
|
||||
- Added support for Cyclone IV family synthesis.
|
||||
- Added example of implementation for DE2i-150 board.
|
||||
- Added example of implementation for MAX10 development kit.
|
||||
- Added LFSR example from Asic World.
|
||||
- Added "dffinit -highlow" for mapping to Intel primitives
|
||||
|
||||
|
||||
Yosys 0.6 .. Yosys 0.7
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
Copyright (C) 2012 - 2018 Clifford Wolf <clifford@clifford.at>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@ -21,7 +21,7 @@ Here is a the C++ code for a "hello_world" Yosys command (hello.cc):
|
|||
|
||||
struct HelloWorldPass : public Pass {
|
||||
HelloWorldPass() : Pass("hello_world") { }
|
||||
virtual void execute(vector<string>, Design*) {
|
||||
void execute(vector<string>, Design*) override {
|
||||
log("Hello World!\n");
|
||||
}
|
||||
} HelloWorldPass;
|
||||
|
@ -373,6 +373,7 @@ Finally run all tests with "make config-{clang,gcc,gcc-4.8}":
|
|||
cd ~yosys
|
||||
make clean
|
||||
make test
|
||||
make ystests
|
||||
make vloghtb
|
||||
make install
|
||||
|
||||
|
@ -411,3 +412,98 @@ Updating the website:
|
|||
git commit -am update
|
||||
make push
|
||||
|
||||
|
||||
|
||||
Cross-Building for Windows with MXE
|
||||
===================================
|
||||
|
||||
Check http://mxe.cc/#requirements and install all missing requirements.
|
||||
|
||||
As root (or other user with write access to /usr/local/src):
|
||||
|
||||
cd /usr/local/src
|
||||
git clone https://github.com/mxe/mxe.git
|
||||
cd mxe
|
||||
|
||||
make -j$(nproc) MXE_PLUGIN_DIRS="plugins/tcl.tk" \
|
||||
MXE_TARGETS="i686-w64-mingw32.static" \
|
||||
gcc tcl readline
|
||||
|
||||
Then as regular user in some directory where you build stuff:
|
||||
|
||||
git clone https://github.com/cliffordwolf/yosys.git yosys-win32
|
||||
cd yosys-win32
|
||||
make config-mxe
|
||||
make -j$(nproc) mxebin
|
||||
|
||||
|
||||
|
||||
How to add unit test
|
||||
====================
|
||||
|
||||
Unit test brings some advantages, briefly, we can list some of them (reference
|
||||
[1](https://en.wikipedia.org/wiki/Unit_testing)):
|
||||
|
||||
* Tests reduce bugs in new features;
|
||||
* Tests reduce bugs in existing features;
|
||||
* Tests are good documentation;
|
||||
* Tests reduce the cost of change;
|
||||
* Tests allow refactoring;
|
||||
|
||||
With those advantages in mind, it was required to choose a framework which fits
|
||||
well with C/C++ code. Hence, it was chosen (google test)
|
||||
[https://github.com/google/googletest], because it is largely used and it is
|
||||
relatively easy learn.
|
||||
|
||||
Install and configure google test (manually)
|
||||
--------------------------------------------
|
||||
|
||||
In this section, you will see a brief description of how to install google
|
||||
test. However, it is strongly recommended that you take a look to the official
|
||||
repository (https://github.com/google/googletest) and refers to that if you
|
||||
have any problem to install it. Follow the steps below:
|
||||
|
||||
* Install: cmake and pthread
|
||||
* Clone google test project from: https://github.com/google/googletest and
|
||||
enter in the project directory
|
||||
* Inside project directory, type:
|
||||
|
||||
```
|
||||
cmake -DBUILD_SHARED_LIBS=ON .
|
||||
make
|
||||
```
|
||||
|
||||
* After compilation, copy all "*.so" inside directory "googlemock" and
|
||||
"googlemock/gtest" to "/usr/lib/"
|
||||
* Done! Now you can compile your tests.
|
||||
|
||||
If you have any problem, go to the official repository to find help.
|
||||
|
||||
Ps.: Some distros already have googletest packed. If your distro supports it,
|
||||
you can use it instead of compile.
|
||||
|
||||
Create new unit test
|
||||
--------------------
|
||||
|
||||
If you want to add new unit tests for Yosys, just follow the steps below:
|
||||
|
||||
* Go to directory "yosys/test/unit/"
|
||||
* In this directory you can find something similar Yosys's directory structure.
|
||||
To create your unit test file you have to follow this pattern:
|
||||
fileNameToImplementUnitTest + Test.cc. E.g.: if you want to implement the
|
||||
unit test for kernel/celledges.cc, you will need to create a file like this:
|
||||
tests/unit/kernel/celledgesTest.cc;
|
||||
* Implement your unit test
|
||||
|
||||
Run unit test
|
||||
-------------
|
||||
|
||||
To compile and run all unit tests, just go to yosys root directory and type:
|
||||
```
|
||||
make unit-test
|
||||
```
|
||||
|
||||
If you want to remove all unit test files, type:
|
||||
```
|
||||
make clean-unit-test
|
||||
```
|
||||
|
|
315
yosys/Makefile
315
yosys/Makefile
|
@ -5,19 +5,29 @@
|
|||
# CONFIG := emcc
|
||||
# CONFIG := mxe
|
||||
# CONFIG := msys2
|
||||
# CONFIG := msys2-64
|
||||
|
||||
# features (the more the better)
|
||||
ENABLE_TCL := 1
|
||||
ENABLE_ABC := 1
|
||||
ENABLE_PLUGINS := 1
|
||||
ENABLE_READLINE := 1
|
||||
ENABLE_EDITLINE := 0
|
||||
ENABLE_VERIFIC := 0
|
||||
ENABLE_COVER := 1
|
||||
ENABLE_LIBYOSYS := 0
|
||||
ENABLE_PROTOBUF := 0
|
||||
|
||||
# other configuration flags
|
||||
ENABLE_GCOV := 0
|
||||
ENABLE_GPROF := 0
|
||||
ENABLE_DEBUG := 0
|
||||
ENABLE_NDEBUG := 0
|
||||
LINK_CURSES := 0
|
||||
LINK_TERMCAP := 0
|
||||
LINK_ABC := 0
|
||||
# Needed for environments that don't have proper thread support (i.e. emscripten)
|
||||
DISABLE_ABC_THREADS := 0
|
||||
|
||||
# clang sanitizers
|
||||
SANITIZER =
|
||||
|
@ -27,6 +37,7 @@ SANITIZER =
|
|||
# SANITIZER = cfi
|
||||
|
||||
|
||||
OS := $(shell uname -s)
|
||||
PREFIX ?= /usr/local
|
||||
INSTALL_SUDO :=
|
||||
|
||||
|
@ -44,35 +55,52 @@ TARGETS = yosys$(EXE) yosys-config
|
|||
PRETTY = 1
|
||||
SMALL = 0
|
||||
|
||||
# Unit test
|
||||
UNITESTPATH := tests/unit
|
||||
|
||||
all: top-all
|
||||
|
||||
YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST)))
|
||||
VPATH := $(YOSYS_SRC)
|
||||
|
||||
CXXFLAGS += -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -D_YOSYS_ -fPIC -I$(PREFIX)/include
|
||||
LDFLAGS += -L$(LIBDIR)
|
||||
LDLIBS = -lstdc++ -lm
|
||||
CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -D_YOSYS_ -fPIC -I$(PREFIX)/include
|
||||
LDFLAGS := $(LDFLAGS) -L$(LIBDIR)
|
||||
LDLIBS := $(LDLIBS) -lstdc++ -lm
|
||||
PLUGIN_LDFLAGS :=
|
||||
|
||||
PKG_CONFIG = pkg-config
|
||||
SED = sed
|
||||
BISON = bison
|
||||
PKG_CONFIG ?= pkg-config
|
||||
SED ?= sed
|
||||
BISON ?= bison
|
||||
STRIP ?= strip
|
||||
AWK ?= awk
|
||||
|
||||
ifeq ($(OS), Darwin)
|
||||
PLUGIN_LDFLAGS += -undefined dynamic_lookup
|
||||
|
||||
# homebrew search paths
|
||||
ifneq ($(shell which brew),)
|
||||
BREW_PREFIX := $(shell brew --prefix)/opt
|
||||
CXXFLAGS += -I$(BREW_PREFIX)/readline/include
|
||||
LDFLAGS += -L$(BREW_PREFIX)/readline/lib
|
||||
PKG_CONFIG_PATH := $(BREW_PREFIX)/libffi/lib/pkgconfig:$(PKG_CONFIG_PATH)
|
||||
PKG_CONFIG_PATH := $(BREW_PREFIX)/tcl-tk/lib/pkgconfig:$(PKG_CONFIG_PATH)
|
||||
export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX)/flex/bin:$(PATH)
|
||||
|
||||
# macports search paths
|
||||
else ifneq ($(shell which port),)
|
||||
PORT_PREFIX := $(patsubst %/bin/port,%,$(shell which port))
|
||||
CXXFLAGS += -I$(PORT_PREFIX)/include
|
||||
LDFLAGS += -L$(PORT_PREFIX)/lib
|
||||
PKG_CONFIG_PATH := $(PORT_PREFIX)/lib/pkgconfig:$(PKG_CONFIG_PATH)
|
||||
export PATH := $(PORT_PREFIX)/bin:$(PATH)
|
||||
endif
|
||||
|
||||
ifeq (Darwin,$(findstring Darwin,$(shell uname)))
|
||||
# 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 = sed
|
||||
else
|
||||
LDFLAGS += -rdynamic
|
||||
LDLIBS += -lrt
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.7
|
||||
YOSYS_VER := 0.8+$(shell cd $(YOSYS_SRC) && test -e .git && { git log --author=clifford@clifford.at --oneline 4d4665b.. | wc -l; })
|
||||
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
|
||||
OBJS = kernel/version_$(GIT_REV).o
|
||||
|
||||
|
@ -82,7 +110,7 @@ OBJS = kernel/version_$(GIT_REV).o
|
|||
# is just a symlink to your actual ABC working directory, as 'make mrproper'
|
||||
# will remove the 'abc' directory and you do not want to accidentally
|
||||
# delete your work on ABC..
|
||||
ABCREV = ae6716b
|
||||
ABCREV = default
|
||||
ABCPULL = 1
|
||||
ABCURL ?= https://github.com/berkeley-abc/abc
|
||||
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1
|
||||
|
@ -105,6 +133,7 @@ ifeq ($(CONFIG),clang)
|
|||
CXX = clang
|
||||
LD = clang++
|
||||
CXXFLAGS += -std=c++11 -Os
|
||||
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
|
||||
|
||||
ifneq ($(SANITIZER),)
|
||||
$(info [Clang Sanitizer] $(SANITIZER))
|
||||
|
@ -127,16 +156,37 @@ else ifeq ($(CONFIG),gcc)
|
|||
CXX = gcc
|
||||
LD = gcc
|
||||
CXXFLAGS += -std=c++11 -Os
|
||||
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
|
||||
|
||||
else ifeq ($(CONFIG),gcc-static)
|
||||
LD = $(CXX)
|
||||
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -static
|
||||
LDLIBS := $(filter-out -lrt,$(LDLIBS))
|
||||
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
||||
CXXFLAGS += -std=c++11 -Os
|
||||
ABCMKARGS = CC="$(CC)" CXX="$(CXX)" LD="$(LD)" ABC_USE_LIBSTDCXX=1 LIBS="-lm -lpthread -static" OPTFLAGS="-O" \
|
||||
ARCHFLAGS="-DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING=1 -Wno-unused-but-set-variable $(ARCHFLAGS)" ABC_USE_NO_READLINE=1
|
||||
ifeq ($(DISABLE_ABC_THREADS),1)
|
||||
ABCMKARGS += "ABC_USE_NO_PTHREADS=1"
|
||||
endif
|
||||
|
||||
else ifeq ($(CONFIG),gcc-4.8)
|
||||
CXX = gcc-4.8
|
||||
LD = gcc-4.8
|
||||
CXXFLAGS += -std=c++11 -Os
|
||||
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
|
||||
|
||||
else ifeq ($(CONFIG),cygwin)
|
||||
CXX = gcc
|
||||
LD = gcc
|
||||
CXXFLAGS += -std=gnu++11 -Os
|
||||
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
|
||||
|
||||
else ifeq ($(CONFIG),emcc)
|
||||
CXX = emcc
|
||||
LD = emcc
|
||||
CXXFLAGS := -std=c++11 $(filter-out -fPIC -ggdb,$(CXXFLAGS))
|
||||
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DABC_MEMALIGN=8"
|
||||
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']"
|
||||
|
@ -150,6 +200,11 @@ EXE = .js
|
|||
TARGETS := $(filter-out yosys-config,$(TARGETS))
|
||||
EXTRA_TARGETS += yosysjs-$(YOSYS_VER).zip
|
||||
|
||||
ifeq ($(ENABLE_ABC),1)
|
||||
LINK_ABC := 1
|
||||
DISABLE_ABC_THREADS := 1
|
||||
endif
|
||||
|
||||
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
|
||||
|
@ -164,29 +219,41 @@ yosys.html: misc/yosys.html
|
|||
$(P) cp misc/yosys.html yosys.html
|
||||
|
||||
else ifeq ($(CONFIG),mxe)
|
||||
CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc
|
||||
LD = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc
|
||||
CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE
|
||||
PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config
|
||||
CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++
|
||||
LD = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++
|
||||
CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE -DYOSYS_MXE_HACKS -Wno-attributes
|
||||
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 -DHAVE_STRUCT_TIMESPEC -x c++ -fpermissive -w"
|
||||
ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" ABC_USE_NO_READLINE=1 CC="$(CXX)" CXX="$(CXX)"
|
||||
ABCMKARGS += ARCHFLAGS="-DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
|
||||
ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" ABC_USE_NO_READLINE=1 CC="/usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc"
|
||||
EXE = .exe
|
||||
|
||||
else ifeq ($(CONFIG),msys2)
|
||||
CXX = i686-w64-mingw32-gcc
|
||||
LD = i686-w64-mingw32-gcc
|
||||
CXX = i686-w64-mingw32-g++
|
||||
LD = i686-w64-mingw32-g++
|
||||
CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR
|
||||
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 -DHAVE_STRUCT_TIMESPEC -x c++ -fpermissive -w"
|
||||
ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" ABC_USE_NO_READLINE=0 CC="$(CXX)" CXX="$(CXX)"
|
||||
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
|
||||
ABCMKARGS += LIBS="-lpthread -s" ABC_USE_NO_READLINE=0 CC="i686-w64-mingw32-gcc" CXX="$(CXX)"
|
||||
EXE = .exe
|
||||
|
||||
else ifeq ($(CONFIG),msys2-64)
|
||||
CXX = x86_64-w64-mingw32-g++
|
||||
LD = x86_64-w64-mingw32-g++
|
||||
CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR
|
||||
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
||||
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
|
||||
LDLIBS := $(filter-out -lrt,$(LDLIBS))
|
||||
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
|
||||
ABCMKARGS += LIBS="-lpthread -s" ABC_USE_NO_READLINE=0 CC="x86_64-w64-mingw32-gcc" CXX="$(CXX)"
|
||||
EXE = .exe
|
||||
|
||||
else ifneq ($(CONFIG),none)
|
||||
$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, gcc-4.8, emcc, mxe, msys2)
|
||||
$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, gcc-4.8, emcc, mxe, msys2, msys2-64)
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_LIBYOSYS),1)
|
||||
|
@ -195,22 +262,67 @@ endif
|
|||
|
||||
ifeq ($(ENABLE_READLINE),1)
|
||||
CXXFLAGS += -DYOSYS_ENABLE_READLINE
|
||||
ifeq ($(OS), FreeBSD)
|
||||
CXXFLAGS += -I/usr/local/include
|
||||
endif
|
||||
LDLIBS += -lreadline
|
||||
ifeq ($(LINK_CURSES),1)
|
||||
LDLIBS += -lcurses
|
||||
ABCMKARGS += "ABC_READLINE_LIBRARIES=-lcurses -lreadline"
|
||||
endif
|
||||
ifeq ($(LINK_TERMCAP),1)
|
||||
LDLIBS += -ltermcap
|
||||
ABCMKARGS += "ABC_READLINE_LIBRARIES=-lreadline -ltermcap"
|
||||
endif
|
||||
ifeq ($(CONFIG),mxe)
|
||||
LDLIBS += -lpdcurses
|
||||
LDLIBS += -ltermcap
|
||||
endif
|
||||
else
|
||||
ifeq ($(ENABLE_EDITLINE),1)
|
||||
CXXFLAGS += -DYOSYS_ENABLE_EDITLINE
|
||||
LDLIBS += -ledit -ltinfo -lbsd
|
||||
else
|
||||
ABCMKARGS += "ABC_USE_NO_READLINE=1"
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(DISABLE_ABC_THREADS),1)
|
||||
ABCMKARGS += "ABC_USE_NO_PTHREADS=1"
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_PLUGINS),1)
|
||||
CXXFLAGS += -DYOSYS_ENABLE_PLUGINS $(shell $(PKG_CONFIG) --silence-errors --cflags libffi)
|
||||
LDLIBS += $(shell $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi) -ldl
|
||||
CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags libffi) -DYOSYS_ENABLE_PLUGINS
|
||||
LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi)
|
||||
ifneq ($(OS), FreeBSD)
|
||||
LDLIBS += -ldl
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_TCL),1)
|
||||
TCL_VERSION ?= tcl$(shell bash -c "tclsh <(echo 'puts [info tclversion]')")
|
||||
ifeq ($(OS), FreeBSD)
|
||||
TCL_INCLUDE ?= /usr/local/include/$(TCL_VERSION)
|
||||
else
|
||||
TCL_INCLUDE ?= /usr/include/$(TCL_VERSION)
|
||||
CXXFLAGS += -I$(TCL_INCLUDE) -DYOSYS_ENABLE_TCL
|
||||
LDLIBS += -l$(TCL_VERSION)
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG),mxe)
|
||||
CXXFLAGS += -DYOSYS_ENABLE_TCL
|
||||
LDLIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz
|
||||
else
|
||||
CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags tcl || echo -I$(TCL_INCLUDE)) -DYOSYS_ENABLE_TCL
|
||||
ifeq ($(OS), FreeBSD)
|
||||
# FreeBSD uses tcl8.6, but lib is named "libtcl86"
|
||||
LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo -l$(TCL_VERSION) | tr -d '.')
|
||||
else
|
||||
LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo -l$(TCL_VERSION))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_GCOV),1)
|
||||
CXXFLAGS += --coverage
|
||||
LDFLAGS += --coverage
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_GPROF),1)
|
||||
|
@ -219,21 +331,44 @@ LDFLAGS += -pg
|
|||
endif
|
||||
|
||||
ifeq ($(ENABLE_NDEBUG),1)
|
||||
CXXFLAGS := -O3 -DNDEBUG $(filter-out -Os,$(CXXFLAGS))
|
||||
CXXFLAGS := -O3 -DNDEBUG $(filter-out -Os -ggdb,$(CXXFLAGS))
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_DEBUG),1)
|
||||
ifeq ($(CONFIG),clang)
|
||||
CXXFLAGS := -O0 -DDEBUG $(filter-out -Os,$(CXXFLAGS))
|
||||
else
|
||||
CXXFLAGS := -Og -DDEBUG $(filter-out -Os,$(CXXFLAGS))
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_ABC),1)
|
||||
CXXFLAGS += -DYOSYS_ENABLE_ABC
|
||||
ifeq ($(LINK_ABC),1)
|
||||
CXXFLAGS += -DYOSYS_LINK_ABC
|
||||
ifeq ($(DISABLE_ABC_THREADS),0)
|
||||
LDLIBS += -lpthread
|
||||
endif
|
||||
else
|
||||
ifeq ($(ABCEXTERNAL),)
|
||||
TARGETS += yosys-abc$(EXE)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_VERIFIC),1)
|
||||
VERIFIC_DIR ?= /usr/local/src/verific_lib_eval
|
||||
VERIFIC_COMPONENTS ?= verilog vhdl database util containers sdf
|
||||
VERIFIC_DIR ?= /usr/local/src/verific_lib
|
||||
VERIFIC_COMPONENTS ?= verilog vhdl database util containers hier_tree
|
||||
CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC
|
||||
LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS))
|
||||
ifeq ($(OS), Darwin)
|
||||
LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)) -lz
|
||||
else
|
||||
LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -lz
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_PROTOBUF),1)
|
||||
LDLIBS += $(shell pkg-config --cflags --libs protobuf)
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_COVER),1)
|
||||
|
@ -261,8 +396,8 @@ endef
|
|||
ifeq ($(PRETTY), 1)
|
||||
P_STATUS = 0
|
||||
P_OFFSET = 0
|
||||
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_UPDATE = $(eval P_STATUS=$(shell echo $(OBJS) yosys$(EXE) | $(AWK) 'BEGIN { RS = " "; I = $(P_STATUS)+0; } $$1 == "$@" && NR > I { I = NR; } END { print I; }'))
|
||||
P_SHOW = [$(shell $(AWK) "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
|
||||
|
@ -347,6 +482,10 @@ include techlibs/common/Makefile.inc
|
|||
|
||||
endif
|
||||
|
||||
ifeq ($(LINK_ABC),1)
|
||||
OBJS += yosys-libabc.a
|
||||
endif
|
||||
|
||||
top-all: $(TARGETS) $(EXTRA_TARGETS)
|
||||
@echo ""
|
||||
@echo " Build successful."
|
||||
|
@ -377,19 +516,30 @@ kernel/version_$(GIT_REV).cc: $(YOSYS_SRC)/Makefile
|
|||
$(P) rm -f kernel/version_*.o kernel/version_*.d kernel/version_*.cc
|
||||
$(Q) mkdir -p kernel && echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"$(YOSYS_VER_STR)\"; }" > kernel/version_$(GIT_REV).cc
|
||||
|
||||
ifeq ($(ENABLE_VERIFIC),1)
|
||||
CXXFLAGS_NOVERIFIC = $(foreach v,$(CXXFLAGS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v)))
|
||||
LDLIBS_NOVERIFIC = $(foreach v,$(LDLIBS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v)))
|
||||
else
|
||||
CXXFLAGS_NOVERIFIC = $(CXXFLAGS)
|
||||
LDLIBS_NOVERIFIC = $(LDLIBS)
|
||||
endif
|
||||
|
||||
yosys-config: misc/yosys-config.in
|
||||
$(P) $(SED) -e 's#@CXXFLAGS@#$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(CXXFLAGS))#;' \
|
||||
-e 's#@CXX@#$(CXX)#;' -e 's#@LDFLAGS@#$(LDFLAGS)#;' -e 's#@LDLIBS@#$(LDLIBS)#;' \
|
||||
-e 's#@BINDIR@#$(BINDIR)#;' -e 's#@DATDIR@#$(DATDIR)#;' < $< > yosys-config
|
||||
$(P) $(SED) -e 's#@CXXFLAGS@#$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(strip $(CXXFLAGS_NOVERIFIC)))#;' \
|
||||
-e 's#@CXX@#$(strip $(CXX))#;' -e 's#@LDFLAGS@#$(strip $(LDFLAGS) $(PLUGIN_LDFLAGS))#;' -e 's#@LDLIBS@#$(strip $(LDLIBS_NOVERIFIC))#;' \
|
||||
-e 's#@BINDIR@#$(strip $(BINDIR))#;' -e 's#@DATDIR@#$(strip $(DATDIR))#;' < $< > yosys-config
|
||||
$(Q) chmod +x yosys-config
|
||||
|
||||
abc/abc-$(ABCREV)$(EXE) abc/libabc-$(ABCREV).a:
|
||||
$(P)
|
||||
ifneq ($(ABCREV),default)
|
||||
$(Q) if ( cd abc 2> /dev/null && hg identify; ) | grep -q +; then \
|
||||
$(Q) if test -d abc/.hg; then \
|
||||
echo 'REEBE: NOP qverpgbel vf n ut jbexvat pbcl! Erzbir nop/ naq er-eha "znxr".' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \
|
||||
fi
|
||||
$(Q) if ( cd abc 2> /dev/null && ! git diff-index --quiet HEAD; ); then \
|
||||
echo 'REEBE: NOP pbagnvaf ybpny zbqvsvpngvbaf! Frg NOPERI=qrsnhyg va Lbflf Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \
|
||||
fi
|
||||
$(Q) if test "`cd abc 2> /dev/null && hg identify | cut -f1 -d' '`" != "$(ABCREV)"; then \
|
||||
$(Q) if test "`cd abc 2> /dev/null && git rev-parse --short HEAD`" != "$(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 $(ABCURL):"; set -x; \
|
||||
test -d abc || git clone $(ABCURL) abc; \
|
||||
|
@ -407,6 +557,9 @@ endif
|
|||
yosys-abc$(EXE): abc/abc-$(ABCREV)$(EXE)
|
||||
$(P) cp abc/abc-$(ABCREV)$(EXE) yosys-abc$(EXE)
|
||||
|
||||
yosys-libabc.a: abc/libabc-$(ABCREV).a
|
||||
$(P) cp abc/libabc-$(ABCREV).a yosys-libabc.a
|
||||
|
||||
ifneq ($(SEED),)
|
||||
SEEDOPT="-S $(SEED)"
|
||||
else
|
||||
|
@ -425,6 +578,8 @@ test: $(TARGETS) $(EXTRA_TARGETS)
|
|||
+cd tests/bram && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/various && bash run-test.sh
|
||||
+cd tests/sat && bash run-test.sh
|
||||
+cd tests/svinterfaces && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/opt && bash run-test.sh
|
||||
@echo ""
|
||||
@echo " Passed \"make test\"."
|
||||
@echo ""
|
||||
|
@ -443,18 +598,44 @@ vloghtb: $(TARGETS) $(EXTRA_TARGETS)
|
|||
@echo " Passed \"make vloghtb\"."
|
||||
@echo ""
|
||||
|
||||
ystests: $(TARGETS) $(EXTRA_TARGETS)
|
||||
rm -rf tests/ystests
|
||||
git clone https://github.com/YosysHQ/yosys-tests.git tests/ystests
|
||||
+$(MAKE) PATH="$$PWD:$$PATH" -C tests/ystests
|
||||
@echo ""
|
||||
@echo " Finished \"make ystests\"."
|
||||
@echo ""
|
||||
|
||||
# Unit test
|
||||
unit-test: libyosys.so
|
||||
@$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CPPFLAGS="$(CPPFLAGS)" \
|
||||
CXXFLAGS="$(CXXFLAGS)" LDLIBS="$(LDLIBS)" ROOTPATH="$(CURDIR)"
|
||||
|
||||
clean-unit-test:
|
||||
@$(MAKE) -C $(UNITESTPATH) clean
|
||||
|
||||
install: $(TARGETS) $(EXTRA_TARGETS)
|
||||
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL_SUDO) install $(TARGETS) $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL_SUDO) cp $(TARGETS) $(DESTDIR)$(BINDIR)
|
||||
ifneq ($(filter yosys,$(TARGETS)),)
|
||||
$(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(BINDIR)/yosys
|
||||
endif
|
||||
ifneq ($(filter yosys-abc,$(TARGETS)),)
|
||||
$(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/yosys-abc
|
||||
endif
|
||||
ifneq ($(filter yosys-filterlib,$(TARGETS)),)
|
||||
$(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/yosys-filterlib
|
||||
endif
|
||||
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(DATDIR)
|
||||
$(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(DATDIR)/.
|
||||
ifeq ($(ENABLE_LIBYOSYS),1)
|
||||
$(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)
|
||||
$(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so
|
||||
$(INSTALL_SUDO) ldconfig
|
||||
endif
|
||||
|
||||
uninstall:
|
||||
$(INSTALL_SUDO) rm -vf $(addprefix $(DESTDIR)$(BINDIR),$(notdir $(TARGETS)))
|
||||
$(INSTALL_SUDO) rm -vf $(addprefix $(DESTDIR)$(BINDIR)/,$(notdir $(TARGETS)))
|
||||
$(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR)
|
||||
ifeq ($(ENABLE_LIBYOSYS),1)
|
||||
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so
|
||||
|
@ -472,16 +653,31 @@ clean:
|
|||
rm -rf share
|
||||
if test -d manual; then cd manual && sh clean.sh; fi
|
||||
rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS)
|
||||
rm -f kernel/version_*.o kernel/version_*.cc abc/abc-[0-9a-f]*
|
||||
rm -f kernel/version_*.o kernel/version_*.cc abc/abc-[0-9a-f]* abc/libabc-[0-9a-f]*.a
|
||||
rm -f libs/*/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d techlibs/*/*.d
|
||||
rm -rf tests/asicworld/*.out tests/asicworld/*.log
|
||||
rm -rf tests/hana/*.out tests/hana/*.log
|
||||
rm -rf tests/simple/*.out tests/simple/*.log
|
||||
rm -rf tests/memories/*.out tests/memories/*.log tests/memories/*.dmp
|
||||
rm -rf tests/sat/*.log tests/techmap/*.log tests/various/*.log
|
||||
rm -rf tests/bram/temp tests/fsm/temp tests/realmath/temp tests/share/temp tests/smv/temp
|
||||
rm -rf vloghtb/Makefile vloghtb/refdat vloghtb/rtl vloghtb/scripts vloghtb/spec vloghtb/check_yosys vloghtb/vloghammer_tb.tar.bz2 vloghtb/temp vloghtb/log_test_*
|
||||
rm -f tests/svinterfaces/*.log_stdout tests/svinterfaces/*.log_stderr tests/svinterfaces/dut_result.txt tests/svinterfaces/reference_result.txt tests/svinterfaces/a.out tests/svinterfaces/*_syn.v tests/svinterfaces/*.diff
|
||||
rm -f tests/tools/cmp_tbdata
|
||||
|
||||
clean-abc:
|
||||
$(MAKE) -C abc DEP= clean
|
||||
rm -f yosys-abc$(EXE) abc/abc-[0-9a-f]*
|
||||
rm -f yosys-abc$(EXE) yosys-libabc.a abc/abc-[0-9a-f]* abc/libabc-[0-9a-f]*.a
|
||||
|
||||
mrproper: clean
|
||||
git clean -xdf
|
||||
|
||||
coverage:
|
||||
./yosys -qp 'help; help -all'
|
||||
rm -rf coverage.info coverage_html
|
||||
lcov --capture -d . --no-external -o coverage.info
|
||||
genhtml coverage.info --output-directory coverage_html
|
||||
|
||||
qtcreator:
|
||||
{ for file in $(basename $(OBJS)); do \
|
||||
for prefix in cc y l; do if [ -f $${file}.$${prefix} ]; then echo $$file.$${prefix}; fi; done \
|
||||
|
@ -521,6 +717,12 @@ config-clang: clean
|
|||
config-gcc: clean
|
||||
echo 'CONFIG := gcc' > Makefile.conf
|
||||
|
||||
config-gcc-static: clean
|
||||
echo 'CONFIG := gcc-static' > Makefile.conf
|
||||
echo 'ENABLE_PLUGINS := 0' >> Makefile.conf
|
||||
echo 'ENABLE_READLINE := 0' >> Makefile.conf
|
||||
echo 'ENABLE_TCL := 0' >> Makefile.conf
|
||||
|
||||
config-gcc-4.8: clean
|
||||
echo 'CONFIG := gcc-4.8' > Makefile.conf
|
||||
|
||||
|
@ -533,13 +735,22 @@ config-emcc: clean
|
|||
|
||||
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-msys2: clean
|
||||
echo 'CONFIG := msys2' > Makefile.conf
|
||||
|
||||
config-msys2-64: clean
|
||||
echo 'CONFIG := msys2-64' > Makefile.conf
|
||||
|
||||
config-cygwin: clean
|
||||
echo 'CONFIG := cygwin' > Makefile.conf
|
||||
|
||||
config-gcov: clean
|
||||
echo 'CONFIG := gcc' > Makefile.conf
|
||||
echo 'ENABLE_GCOV := 1' >> Makefile.conf
|
||||
echo 'ENABLE_DEBUG := 1' >> Makefile.conf
|
||||
|
||||
config-gprof: clean
|
||||
echo 'CONFIG := gcc' > Makefile.conf
|
||||
echo 'ENABLE_GPROF := 1' >> Makefile.conf
|
||||
|
@ -560,6 +771,6 @@ echo-git-rev:
|
|||
-include kernel/*.d
|
||||
-include techlibs/*/*.d
|
||||
|
||||
.PHONY: all top-all abc test install install-abc manual clean mrproper qtcreator
|
||||
.PHONY: config-clean config-clang config-gcc config-gcc-4.8 config-gprof config-sudo
|
||||
.PHONY: all top-all abc test install install-abc manual clean mrproper qtcreator coverage vcxsrc mxebin
|
||||
.PHONY: config-clean config-clang config-gcc config-gcc-static config-gcc-4.8 config-gprof config-sudo
|
||||
|
||||
|
|
|
@ -0,0 +1,490 @@
|
|||
```
|
||||
yosys -- Yosys Open SYnthesis Suite
|
||||
|
||||
Copyright (C) 2012 - 2018 Clifford Wolf <clifford@clifford.at>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
```
|
||||
|
||||
|
||||
yosys – Yosys Open SYnthesis Suite
|
||||
===================================
|
||||
|
||||
This is a framework for RTL synthesis tools. It currently has
|
||||
extensive Verilog-2005 support and provides a basic set of
|
||||
synthesis algorithms for various application domains.
|
||||
|
||||
Yosys can be adapted to perform any synthesis job by combining
|
||||
the existing passes (algorithms) using synthesis scripts and
|
||||
adding additional passes as needed by extending the yosys C++
|
||||
code base.
|
||||
|
||||
Yosys is free software licensed under the ISC license (a GPL
|
||||
compatible license that is similar in terms to the MIT license
|
||||
or the 2-clause BSD license).
|
||||
|
||||
|
||||
Web Site
|
||||
========
|
||||
|
||||
More information and documentation can be found on the Yosys web site:
|
||||
http://www.clifford.at/yosys/
|
||||
|
||||
Setup
|
||||
======
|
||||
|
||||
You need a C++ compiler with C++11 support (up-to-date CLANG or GCC is
|
||||
recommended) and some standard tools such as GNU Flex, GNU Bison, and GNU Make.
|
||||
TCL, readline and libffi are optional (see ``ENABLE_*`` settings in Makefile).
|
||||
Xdot (graphviz) is used by the ``show`` command in yosys to display schematics.
|
||||
|
||||
For example on Ubuntu Linux 16.04 LTS the following commands will install all
|
||||
prerequisites for building yosys:
|
||||
|
||||
$ sudo apt-get install build-essential clang bison flex \
|
||||
libreadline-dev gawk tcl-dev libffi-dev git \
|
||||
graphviz xdot pkg-config python3
|
||||
|
||||
Similarily, on Mac OS X MacPorts or Homebrew can be used to install dependencies:
|
||||
|
||||
$ brew tap Homebrew/bundle && brew bundle
|
||||
$ sudo port install bison flex readline gawk libffi \
|
||||
git graphviz pkgconfig python36
|
||||
|
||||
On FreeBSD use the following command to install all prerequisites:
|
||||
|
||||
# pkg install bison flex readline gawk libffi\
|
||||
git graphviz pkgconfig python3 python36 tcl-wrapper
|
||||
|
||||
On FreeBSD system use gmake instead of make. To run tests use:
|
||||
% MAKE=gmake CC=cc gmake test
|
||||
|
||||
For Cygwin use the following command to install all prerequisites, or select these additional packages:
|
||||
|
||||
setup-x86_64.exe -q --packages=bison,flex,gcc-core,gcc-g++,git,libffi-devel,libreadline-devel,make,pkg-config,python3,tcl-devel
|
||||
|
||||
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
|
||||
|
||||
To configure the build system to use a specific compiler, use one of
|
||||
|
||||
$ make config-clang
|
||||
$ make config-gcc
|
||||
|
||||
For other compilers and build configurations it might be
|
||||
necessary to make some changes to the config section of the
|
||||
Makefile.
|
||||
|
||||
$ vi Makefile # ..or..
|
||||
$ vi Makefile.conf
|
||||
|
||||
To build Yosys simply type 'make' in this directory.
|
||||
|
||||
$ make
|
||||
$ make test
|
||||
$ sudo make install
|
||||
|
||||
Note that this also downloads, builds and installs ABC (using yosys-abc
|
||||
as executable name).
|
||||
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
Yosys can be used with the interactive command shell, with
|
||||
synthesis scripts or with command line arguments. Let's perform
|
||||
a simple synthesis job using the interactive command shell:
|
||||
|
||||
$ ./yosys
|
||||
yosys>
|
||||
|
||||
the command ``help`` can be used to print a list of all available
|
||||
commands and ``help <command>`` to print details on the specified command:
|
||||
|
||||
yosys> help help
|
||||
|
||||
reading the design using the Verilog frontend:
|
||||
|
||||
yosys> read_verilog tests/simple/fiedler-cooley.v
|
||||
|
||||
writing the design to the console in Yosys's internal format:
|
||||
|
||||
yosys> write_ilang
|
||||
|
||||
elaborate design hierarchy:
|
||||
|
||||
yosys> hierarchy
|
||||
|
||||
convert processes (``always`` blocks) to netlist elements and perform
|
||||
some simple optimizations:
|
||||
|
||||
yosys> proc; opt
|
||||
|
||||
display design netlist using ``xdot``:
|
||||
|
||||
yosys> show
|
||||
|
||||
the same thing using ``gv`` as postscript viewer:
|
||||
|
||||
yosys> show -format ps -viewer gv
|
||||
|
||||
translating netlist to gate logic and perform some simple optimizations:
|
||||
|
||||
yosys> techmap; opt
|
||||
|
||||
write design netlist to a new Verilog file:
|
||||
|
||||
yosys> write_verilog synth.v
|
||||
|
||||
a similar synthesis can be performed using yosys command line options only:
|
||||
|
||||
$ ./yosys -o synth.v -p hierarchy -p proc -p opt \
|
||||
-p techmap -p opt tests/simple/fiedler-cooley.v
|
||||
|
||||
or using a simple synthesis script:
|
||||
|
||||
$ cat synth.ys
|
||||
read_verilog tests/simple/fiedler-cooley.v
|
||||
hierarchy; proc; opt; techmap; opt
|
||||
write_verilog synth.v
|
||||
|
||||
$ ./yosys synth.ys
|
||||
|
||||
It is also possible to only have the synthesis commands but not the read/write
|
||||
commands in the synthesis script:
|
||||
|
||||
$ cat synth.ys
|
||||
hierarchy; proc; opt; techmap; opt
|
||||
|
||||
$ ./yosys -o synth.v tests/simple/fiedler-cooley.v synth.ys
|
||||
|
||||
The following very basic synthesis script should work well with all designs:
|
||||
|
||||
# check design hierarchy
|
||||
hierarchy
|
||||
|
||||
# translate processes (always blocks)
|
||||
proc; opt
|
||||
|
||||
# detect and optimize FSM encodings
|
||||
fsm; opt
|
||||
|
||||
# implement memories (arrays)
|
||||
memory; opt
|
||||
|
||||
# convert to gate logic
|
||||
techmap; opt
|
||||
|
||||
If ABC is enabled in the Yosys build configuration and a cell library is given
|
||||
in the liberty file ``mycells.lib``, the following synthesis script will
|
||||
synthesize for the given cell library:
|
||||
|
||||
# the high-level stuff
|
||||
hierarchy; proc; fsm; opt; memory; opt
|
||||
|
||||
# mapping to internal cell library
|
||||
techmap; opt
|
||||
|
||||
# mapping flip-flops to mycells.lib
|
||||
dfflibmap -liberty mycells.lib
|
||||
|
||||
# mapping logic to mycells.lib
|
||||
abc -liberty mycells.lib
|
||||
|
||||
# cleanup
|
||||
clean
|
||||
|
||||
If you do not have a liberty file but want to test this synthesis script,
|
||||
you can use the file ``examples/cmos/cmos_cells.lib`` from the yosys sources.
|
||||
|
||||
Liberty file downloads for and information about free and open ASIC standard
|
||||
cell libraries can be found here:
|
||||
|
||||
- http://www.vlsitechnology.org/html/libraries.html
|
||||
- http://www.vlsitechnology.org/synopsys/vsclib013.lib
|
||||
|
||||
The command ``synth`` provides a good default synthesis script (see
|
||||
``help synth``). If possible a synthesis script should borrow from ``synth``.
|
||||
For example:
|
||||
|
||||
# the high-level stuff
|
||||
hierarchy
|
||||
synth -run coarse
|
||||
|
||||
# mapping to internal cells
|
||||
techmap; opt -fast
|
||||
dfflibmap -liberty mycells.lib
|
||||
abc -liberty mycells.lib
|
||||
clean
|
||||
|
||||
Yosys is under construction. A more detailed documentation will follow.
|
||||
|
||||
|
||||
Unsupported Verilog-2005 Features
|
||||
=================================
|
||||
|
||||
The following Verilog-2005 features are not supported by
|
||||
Yosys and there are currently no plans to add support
|
||||
for them:
|
||||
|
||||
- Non-synthesizable language features as defined in
|
||||
IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002
|
||||
|
||||
- The ``tri``, ``triand``, ``trior``, ``wand`` and ``wor`` net types
|
||||
|
||||
- The ``config`` keyword and library map files
|
||||
|
||||
- The ``disable``, ``primitive`` and ``specify`` statements
|
||||
|
||||
- Latched logic (is synthesized as logic with feedback loops)
|
||||
|
||||
|
||||
Verilog Attributes and non-standard features
|
||||
============================================
|
||||
|
||||
- The ``full_case`` attribute on case statements is supported
|
||||
(also the non-standard ``// synopsys full_case`` directive)
|
||||
|
||||
- The ``parallel_case`` attribute on case statements is supported
|
||||
(also the non-standard ``// synopsys parallel_case`` directive)
|
||||
|
||||
- The ``// synopsys translate_off`` and ``// synopsys translate_on``
|
||||
directives are also supported (but the use of ``` `ifdef .. `endif ```
|
||||
is strongly recommended instead).
|
||||
|
||||
- The ``nomem2reg`` attribute on modules or arrays prohibits the
|
||||
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
|
||||
not affect clocked storage elements such as flip-flops.
|
||||
|
||||
- The ``nosync`` attribute on registers prohibits the generation of a
|
||||
storage element. The register itself will always have all bits set
|
||||
to 'x' (undefined). The variable may only be used as blocking assigned
|
||||
temporary variable within an always block. This is mostly used internally
|
||||
by Yosys to synthesize Verilog functions and access arrays.
|
||||
|
||||
- The ``onehot`` attribute on wires mark them as one-hot 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
|
||||
passes to identify input and output ports of cells. The Verilog backend
|
||||
also does not output blackbox modules on default.
|
||||
|
||||
- The ``keep`` attribute on cells and wires is used to mark objects that should
|
||||
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 to add the necessary reset logic.
|
||||
|
||||
- The ``top`` attribute on a module marks this module as the top of the
|
||||
design hierarchy. The ``hierarchy`` command sets this attribute when called
|
||||
with ``-top``. Other commands, such as ``flatten`` and various backends
|
||||
use this attribute to determine the top module.
|
||||
|
||||
- The ``src`` attribute is set on cells and wires created by to the string
|
||||
``<hdl-file-name>:<line-number>`` by the HDL front-end and is then carried
|
||||
through the synthesis. When entities are combined, a new |-separated
|
||||
string is created that contains all the string from the original entities.
|
||||
|
||||
- In addition to the ``(* ... *)`` attribute syntax, Yosys supports
|
||||
the non-standard ``{* ... *}`` attribute syntax to set default attributes
|
||||
for everything that comes after the ``{* ... *}`` statement. (Reset
|
||||
by adding an empty ``{* *}`` statement.)
|
||||
|
||||
- In module parameter and port declarations, and cell port and parameter
|
||||
lists, a trailing comma is ignored. This simplifies writing Verilog code
|
||||
generators a bit in some cases.
|
||||
|
||||
- Modules can be declared with ``module mod_name(...);`` (with three dots
|
||||
instead of a list of module ports). With this syntax it is sufficient
|
||||
to simply declare a module port as 'input' or 'output' in the module
|
||||
body.
|
||||
|
||||
- When defining a macro with `define, all text between triple double quotes
|
||||
is interpreted as macro body, even if it contains unescaped newlines. The
|
||||
tipple double quotes are removed from the macro body. For example:
|
||||
|
||||
`define MY_MACRO(a, b) """
|
||||
assign a = 23;
|
||||
assign b = 42;
|
||||
"""
|
||||
|
||||
- The attribute ``via_celltype`` can be used to implement a Verilog task or
|
||||
function by instantiating the specified cell type. The value is the name
|
||||
of the cell type to use. For functions the name of the output port can
|
||||
be specified by appending it to the cell type separated by a whitespace.
|
||||
The body of the task or function is unused in this case and can be used
|
||||
to specify a behavioral model of the cell type for simulation. For example:
|
||||
|
||||
module my_add3(A, B, C, Y);
|
||||
parameter WIDTH = 8;
|
||||
input [WIDTH-1:0] A, B, C;
|
||||
output [WIDTH-1:0] Y;
|
||||
...
|
||||
endmodule
|
||||
|
||||
module top;
|
||||
...
|
||||
(* via_celltype = "my_add3 Y" *)
|
||||
(* via_celltype_defparam_WIDTH = 32 *)
|
||||
function [31:0] add3;
|
||||
input [31:0] A, B, C;
|
||||
begin
|
||||
add3 = A + B + C;
|
||||
end
|
||||
endfunction
|
||||
...
|
||||
endmodule
|
||||
|
||||
- A limited subset of DPI-C functions is supported. The plugin mechanism
|
||||
(see ``help plugin``) can be used to load .so files with implementations
|
||||
of DPI-C routines. As a non-standard extension it is possible to specify
|
||||
a plugin alias using the ``<alias>:`` syntax. For example:
|
||||
|
||||
module dpitest;
|
||||
import "DPI-C" function foo:round = real my_round (real);
|
||||
parameter real r = my_round(12.345);
|
||||
endmodule
|
||||
|
||||
$ yosys -p 'plugin -a foo -i /lib/libm.so; read_verilog dpitest.v'
|
||||
|
||||
- Sized constants (the syntax ``<size>'s?[bodh]<value>``) support constant
|
||||
expressions as <size>. If the expression is not a simple identifier, it
|
||||
must be put in parentheses. Examples: ``WIDTH'd42``, ``(4+2)'b101010``
|
||||
|
||||
- The system tasks ``$finish`` and ``$display`` are supported in initial blocks
|
||||
in an unconditional context (only if/case statements on parameters
|
||||
and constant values). The intended use for this is synthesis-time DRC.
|
||||
|
||||
|
||||
Non-standard or SystemVerilog features for formal verification
|
||||
==============================================================
|
||||
|
||||
- Support for ``assert``, ``assume``, ``restrict``, and ``cover`` is enabled
|
||||
when ``read_verilog`` is called with ``-formal``.
|
||||
|
||||
- The system task ``$initstate`` evaluates to 1 in the initial state and
|
||||
to 0 otherwise.
|
||||
|
||||
- The system function ``$anyconst`` evaluates to any constant value. This is
|
||||
equivalent to declaring a reg as ``rand const``, but also works outside
|
||||
of checkers. (Yosys also supports ``rand const`` outside checkers.)
|
||||
|
||||
- The system function ``$anyseq`` evaluates to any value, possibly a different
|
||||
value in each cycle. This is equivalent to declaring a reg as ``rand``,
|
||||
but also works outside of checkers. (Yosys also supports ``rand``
|
||||
variables outside checkers.)
|
||||
|
||||
- The system functions ``$allconst`` and ``$allseq`` can be used to construct
|
||||
formal exist-forall problems. Assumptions only hold if the trace satisfies
|
||||
the assumption for all ``$allconst/$allseq`` values. For assertions and cover
|
||||
statements it is sufficient if just one ``$allconst/$allseq`` value triggers
|
||||
the property (similar to ``$anyconst/$anyseq``).
|
||||
|
||||
- Wires/registers declared using the ``anyconst/anyseq/allconst/allseq`` attribute
|
||||
(for example ``(* anyconst *) reg [7:0] foobar;``) will behave as if driven
|
||||
by a ``$anyconst/$anyseq/$allconst/$allseq`` function.
|
||||
|
||||
- The SystemVerilog tasks ``$past``, ``$stable``, ``$rose`` and ``$fell`` are
|
||||
supported in any clocked block.
|
||||
|
||||
- The syntax ``@($global_clock)`` can be used to create FFs that have no
|
||||
explicit clock input ($ff cells). The same can be achieved by using
|
||||
``@(posedge <netname>)`` or ``@(negedge <netname>)`` when ``<netname>``
|
||||
is marked with the ``(* gclk *)`` Verilog attribute.
|
||||
|
||||
|
||||
Supported features from SystemVerilog
|
||||
=====================================
|
||||
|
||||
When ``read_verilog`` is called with ``-sv``, it accepts some language features
|
||||
from SystemVerilog:
|
||||
|
||||
- The ``assert`` statement from SystemVerilog is supported in its most basic
|
||||
form. In module context: ``assert property (<expression>);`` and within an
|
||||
always block: ``assert(<expression>);``. It is transformed to a $assert cell.
|
||||
|
||||
- The ``assume``, ``restrict``, and ``cover`` statements from SystemVerilog are
|
||||
also supported. The same limitations as with the ``assert`` statement apply.
|
||||
|
||||
- The keywords ``always_comb``, ``always_ff`` and ``always_latch``, ``logic``
|
||||
and ``bit`` are supported.
|
||||
|
||||
- Declaring free variables with ``rand`` and ``rand const`` is supported.
|
||||
|
||||
- Checkers without a port list that do not need to be instantiated (but instead
|
||||
behave like a named block) are supported.
|
||||
|
||||
- SystemVerilog packages are supported. Once a SystemVerilog file is read
|
||||
into a design with ``read_verilog``, all its packages are available to
|
||||
SystemVerilog files being read into the same design afterwards.
|
||||
|
||||
- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
|
||||
ports are inputs or outputs are supported.
|
||||
|
||||
|
||||
Building the documentation
|
||||
==========================
|
||||
|
||||
Note that there is no need to build the manual if you just want to read it.
|
||||
Simply download the PDF from http://www.clifford.at/yosys/documentation.html
|
||||
instead.
|
||||
|
||||
On Ubuntu, texlive needs these packages to be able to build the manual:
|
||||
|
||||
sudo apt-get install texlive-binaries
|
||||
sudo apt-get install texlive-science # install algorithm2e.sty
|
||||
sudo apt-get install texlive-bibtex-extra # gets multibib.sty
|
||||
sudo apt-get install texlive-fonts-extra # gets skull.sty and dsfont.sty
|
||||
sudo apt-get install texlive-publishers # IEEEtran.cls
|
||||
|
||||
Also the non-free font luximono should be installed, there is unfortunately
|
||||
no Ubuntu package for this so it should be installed separately using
|
||||
`getnonfreefonts`:
|
||||
|
||||
wget https://tug.org/fonts/getnonfreefonts/install-getnonfreefonts
|
||||
sudo texlua install-getnonfreefonts # will install to /usr/local by default, can be changed by editing BINDIR at MANDIR at the top of the script
|
||||
getnonfreefonts luximono # installs to /home/user/texmf
|
||||
|
||||
Then execute, from the root of the repository:
|
||||
|
||||
make manual
|
||||
|
||||
Notes:
|
||||
|
||||
- To run `make manual` you need to have installed Yosys with `make install`,
|
||||
otherwise it will fail on finding `kernel/yosys.h` while building
|
||||
`PRESENTATION_Prog`.
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/aiger/aiger.o
|
||||
|
|
@ -0,0 +1,788 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
void aiger_encode(std::ostream &f, int x)
|
||||
{
|
||||
log_assert(x >= 0);
|
||||
|
||||
while (x & ~0x7f) {
|
||||
f.put((x & 0x7f) | 0x80);
|
||||
x = x >> 7;
|
||||
}
|
||||
|
||||
f.put(x);
|
||||
}
|
||||
|
||||
struct AigerWriter
|
||||
{
|
||||
Module *module;
|
||||
bool zinit_mode;
|
||||
SigMap sigmap;
|
||||
|
||||
dict<SigBit, bool> init_map;
|
||||
pool<SigBit> input_bits, output_bits;
|
||||
dict<SigBit, SigBit> not_map, ff_map, alias_map;
|
||||
dict<SigBit, pair<SigBit, SigBit>> and_map;
|
||||
vector<pair<SigBit, SigBit>> asserts, assumes;
|
||||
vector<pair<SigBit, SigBit>> liveness, fairness;
|
||||
pool<SigBit> initstate_bits;
|
||||
|
||||
vector<pair<int, int>> aig_gates;
|
||||
vector<int> aig_latchin, aig_latchinit, aig_outputs;
|
||||
int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0;
|
||||
int aig_b = 0, aig_c = 0, aig_j = 0, aig_f = 0;
|
||||
|
||||
dict<SigBit, int> aig_map;
|
||||
dict<SigBit, int> ordered_outputs;
|
||||
dict<SigBit, int> ordered_latches;
|
||||
|
||||
dict<SigBit, int> init_inputs;
|
||||
int initstate_ff = 0;
|
||||
|
||||
int mkgate(int a0, int a1)
|
||||
{
|
||||
aig_m++, aig_a++;
|
||||
aig_gates.push_back(a0 > a1 ? make_pair(a0, a1) : make_pair(a1, a0));
|
||||
return 2*aig_m;
|
||||
}
|
||||
|
||||
int bit2aig(SigBit bit)
|
||||
{
|
||||
if (aig_map.count(bit) == 0)
|
||||
{
|
||||
aig_map[bit] = -1;
|
||||
|
||||
if (initstate_bits.count(bit)) {
|
||||
log_assert(initstate_ff > 0);
|
||||
aig_map[bit] = initstate_ff;
|
||||
} else
|
||||
if (not_map.count(bit)) {
|
||||
int a = bit2aig(not_map.at(bit)) ^ 1;
|
||||
aig_map[bit] = a;
|
||||
} else
|
||||
if (and_map.count(bit)) {
|
||||
auto args = and_map.at(bit);
|
||||
int a0 = bit2aig(args.first);
|
||||
int a1 = bit2aig(args.second);
|
||||
aig_map[bit] = mkgate(a0, a1);
|
||||
} else
|
||||
if (alias_map.count(bit)) {
|
||||
aig_map[bit] = bit2aig(alias_map.at(bit));
|
||||
}
|
||||
|
||||
if (bit == State::Sx || bit == State::Sz)
|
||||
log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n");
|
||||
}
|
||||
|
||||
log_assert(aig_map.at(bit) >= 0);
|
||||
return aig_map.at(bit);
|
||||
}
|
||||
|
||||
AigerWriter(Module *module, bool zinit_mode, bool imode, bool omode, bool bmode) : module(module), zinit_mode(zinit_mode), sigmap(module)
|
||||
{
|
||||
pool<SigBit> undriven_bits;
|
||||
pool<SigBit> unused_bits;
|
||||
|
||||
// promote public wires
|
||||
for (auto wire : module->wires())
|
||||
if (wire->name[0] == '\\')
|
||||
sigmap.add(wire);
|
||||
|
||||
// promote input wires
|
||||
for (auto wire : module->wires())
|
||||
if (wire->port_input)
|
||||
sigmap.add(wire);
|
||||
|
||||
// promote output wires
|
||||
for (auto wire : module->wires())
|
||||
if (wire->port_output)
|
||||
sigmap.add(wire);
|
||||
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
if (wire->attributes.count("\\init")) {
|
||||
SigSpec initsig = sigmap(wire);
|
||||
Const initval = wire->attributes.at("\\init");
|
||||
for (int i = 0; i < GetSize(wire) && i < GetSize(initval); i++)
|
||||
if (initval[i] == State::S0 || initval[i] == State::S1)
|
||||
init_map[initsig[i]] = initval[i] == State::S1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < GetSize(wire); i++)
|
||||
{
|
||||
SigBit wirebit(wire, i);
|
||||
SigBit bit = sigmap(wirebit);
|
||||
|
||||
if (bit.wire == nullptr) {
|
||||
if (wire->port_output) {
|
||||
aig_map[wirebit] = (bit == State::S1) ? 1 : 0;
|
||||
output_bits.insert(wirebit);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
undriven_bits.insert(bit);
|
||||
unused_bits.insert(bit);
|
||||
|
||||
if (wire->port_input)
|
||||
input_bits.insert(bit);
|
||||
|
||||
if (wire->port_output) {
|
||||
if (bit != wirebit)
|
||||
alias_map[wirebit] = bit;
|
||||
output_bits.insert(wirebit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto bit : input_bits)
|
||||
undriven_bits.erase(bit);
|
||||
|
||||
for (auto bit : output_bits)
|
||||
unused_bits.erase(bit);
|
||||
|
||||
for (auto cell : module->cells())
|
||||
{
|
||||
if (cell->type == "$_NOT_")
|
||||
{
|
||||
SigBit A = sigmap(cell->getPort("\\A").as_bit());
|
||||
SigBit Y = sigmap(cell->getPort("\\Y").as_bit());
|
||||
unused_bits.erase(A);
|
||||
undriven_bits.erase(Y);
|
||||
not_map[Y] = A;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$_FF_", "$_DFF_N_", "$_DFF_P_"))
|
||||
{
|
||||
SigBit D = sigmap(cell->getPort("\\D").as_bit());
|
||||
SigBit Q = sigmap(cell->getPort("\\Q").as_bit());
|
||||
unused_bits.erase(D);
|
||||
undriven_bits.erase(Q);
|
||||
ff_map[Q] = D;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$_AND_")
|
||||
{
|
||||
SigBit A = sigmap(cell->getPort("\\A").as_bit());
|
||||
SigBit B = sigmap(cell->getPort("\\B").as_bit());
|
||||
SigBit Y = sigmap(cell->getPort("\\Y").as_bit());
|
||||
unused_bits.erase(A);
|
||||
unused_bits.erase(B);
|
||||
undriven_bits.erase(Y);
|
||||
and_map[Y] = make_pair(A, B);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$initstate")
|
||||
{
|
||||
SigBit Y = sigmap(cell->getPort("\\Y").as_bit());
|
||||
undriven_bits.erase(Y);
|
||||
initstate_bits.insert(Y);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$assert")
|
||||
{
|
||||
SigBit A = sigmap(cell->getPort("\\A").as_bit());
|
||||
SigBit EN = sigmap(cell->getPort("\\EN").as_bit());
|
||||
unused_bits.erase(A);
|
||||
unused_bits.erase(EN);
|
||||
asserts.push_back(make_pair(A, EN));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$assume")
|
||||
{
|
||||
SigBit A = sigmap(cell->getPort("\\A").as_bit());
|
||||
SigBit EN = sigmap(cell->getPort("\\EN").as_bit());
|
||||
unused_bits.erase(A);
|
||||
unused_bits.erase(EN);
|
||||
assumes.push_back(make_pair(A, EN));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$live")
|
||||
{
|
||||
SigBit A = sigmap(cell->getPort("\\A").as_bit());
|
||||
SigBit EN = sigmap(cell->getPort("\\EN").as_bit());
|
||||
unused_bits.erase(A);
|
||||
unused_bits.erase(EN);
|
||||
liveness.push_back(make_pair(A, EN));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$fair")
|
||||
{
|
||||
SigBit A = sigmap(cell->getPort("\\A").as_bit());
|
||||
SigBit EN = sigmap(cell->getPort("\\EN").as_bit());
|
||||
unused_bits.erase(A);
|
||||
unused_bits.erase(EN);
|
||||
fairness.push_back(make_pair(A, EN));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$anyconst")
|
||||
{
|
||||
for (auto bit : sigmap(cell->getPort("\\Y"))) {
|
||||
undriven_bits.erase(bit);
|
||||
ff_map[bit] = bit;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$anyseq")
|
||||
{
|
||||
for (auto bit : sigmap(cell->getPort("\\Y"))) {
|
||||
undriven_bits.erase(bit);
|
||||
input_bits.insert(bit);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
|
||||
}
|
||||
|
||||
for (auto bit : unused_bits)
|
||||
undriven_bits.erase(bit);
|
||||
|
||||
if (!undriven_bits.empty()) {
|
||||
undriven_bits.sort();
|
||||
for (auto bit : undriven_bits) {
|
||||
log_warning("Treating undriven bit %s.%s like $anyseq.\n", log_id(module), log_signal(bit));
|
||||
input_bits.insert(bit);
|
||||
}
|
||||
log_warning("Treating a total of %d undriven bits in %s like $anyseq.\n", GetSize(undriven_bits), log_id(module));
|
||||
}
|
||||
|
||||
init_map.sort();
|
||||
input_bits.sort();
|
||||
output_bits.sort();
|
||||
not_map.sort();
|
||||
ff_map.sort();
|
||||
and_map.sort();
|
||||
|
||||
aig_map[State::S0] = 0;
|
||||
aig_map[State::S1] = 1;
|
||||
|
||||
for (auto bit : input_bits) {
|
||||
aig_m++, aig_i++;
|
||||
aig_map[bit] = 2*aig_m;
|
||||
}
|
||||
|
||||
if (imode && input_bits.empty()) {
|
||||
aig_m++, aig_i++;
|
||||
}
|
||||
|
||||
if (zinit_mode)
|
||||
{
|
||||
for (auto it : ff_map) {
|
||||
if (init_map.count(it.first))
|
||||
continue;
|
||||
aig_m++, aig_i++;
|
||||
init_inputs[it.first] = 2*aig_m;
|
||||
}
|
||||
}
|
||||
|
||||
int fair_live_inputs_cnt = GetSize(liveness);
|
||||
int fair_live_inputs_m = aig_m;
|
||||
|
||||
aig_m += fair_live_inputs_cnt;
|
||||
aig_i += fair_live_inputs_cnt;
|
||||
|
||||
for (auto it : ff_map) {
|
||||
aig_m++, aig_l++;
|
||||
aig_map[it.first] = 2*aig_m;
|
||||
ordered_latches[it.first] = aig_l-1;
|
||||
if (init_map.count(it.first) == 0)
|
||||
aig_latchinit.push_back(2);
|
||||
else
|
||||
aig_latchinit.push_back(init_map.at(it.first) ? 1 : 0);
|
||||
}
|
||||
|
||||
if (!initstate_bits.empty() || !init_inputs.empty()) {
|
||||
aig_m++, aig_l++;
|
||||
initstate_ff = 2*aig_m+1;
|
||||
aig_latchinit.push_back(0);
|
||||
}
|
||||
|
||||
int fair_live_latches_cnt = GetSize(fairness) + 2*GetSize(liveness);
|
||||
int fair_live_latches_m = aig_m;
|
||||
int fair_live_latches_l = aig_l;
|
||||
|
||||
aig_m += fair_live_latches_cnt;
|
||||
aig_l += fair_live_latches_cnt;
|
||||
|
||||
for (int i = 0; i < fair_live_latches_cnt; i++)
|
||||
aig_latchinit.push_back(0);
|
||||
|
||||
if (zinit_mode)
|
||||
{
|
||||
for (auto it : ff_map)
|
||||
{
|
||||
int l = ordered_latches[it.first];
|
||||
|
||||
if (aig_latchinit.at(l) == 1)
|
||||
aig_map[it.first] ^= 1;
|
||||
|
||||
if (aig_latchinit.at(l) == 2)
|
||||
{
|
||||
int gated_ffout = mkgate(aig_map[it.first], initstate_ff^1);
|
||||
int gated_initin = mkgate(init_inputs[it.first], initstate_ff);
|
||||
aig_map[it.first] = mkgate(gated_ffout^1, gated_initin^1)^1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it : ff_map) {
|
||||
int a = bit2aig(it.second);
|
||||
int l = ordered_latches[it.first];
|
||||
if (zinit_mode && aig_latchinit.at(l) == 1)
|
||||
aig_latchin.push_back(a ^ 1);
|
||||
else
|
||||
aig_latchin.push_back(a);
|
||||
}
|
||||
|
||||
if (!initstate_bits.empty() || !init_inputs.empty())
|
||||
aig_latchin.push_back(1);
|
||||
|
||||
for (auto bit : output_bits) {
|
||||
aig_o++;
|
||||
ordered_outputs[bit] = aig_o-1;
|
||||
aig_outputs.push_back(bit2aig(bit));
|
||||
}
|
||||
|
||||
if (omode && output_bits.empty()) {
|
||||
aig_o++;
|
||||
aig_outputs.push_back(0);
|
||||
}
|
||||
|
||||
for (auto it : asserts) {
|
||||
aig_b++;
|
||||
int bit_a = bit2aig(it.first);
|
||||
int bit_en = bit2aig(it.second);
|
||||
aig_outputs.push_back(mkgate(bit_a^1, bit_en));
|
||||
}
|
||||
|
||||
if (bmode && asserts.empty()) {
|
||||
aig_b++;
|
||||
aig_outputs.push_back(0);
|
||||
}
|
||||
|
||||
for (auto it : assumes) {
|
||||
aig_c++;
|
||||
int bit_a = bit2aig(it.first);
|
||||
int bit_en = bit2aig(it.second);
|
||||
aig_outputs.push_back(mkgate(bit_a^1, bit_en)^1);
|
||||
}
|
||||
|
||||
for (auto it : liveness)
|
||||
{
|
||||
int input_m = ++fair_live_inputs_m;
|
||||
int latch_m1 = ++fair_live_latches_m;
|
||||
int latch_m2 = ++fair_live_latches_m;
|
||||
|
||||
log_assert(GetSize(aig_latchin) == fair_live_latches_l);
|
||||
fair_live_latches_l += 2;
|
||||
|
||||
int bit_a = bit2aig(it.first);
|
||||
int bit_en = bit2aig(it.second);
|
||||
int bit_s = 2*input_m;
|
||||
int bit_q1 = 2*latch_m1;
|
||||
int bit_q2 = 2*latch_m2;
|
||||
|
||||
int bit_d1 = mkgate(mkgate(bit_s, bit_en)^1, bit_q1^1)^1;
|
||||
int bit_d2 = mkgate(mkgate(bit_d1, bit_a)^1, bit_q2^1)^1;
|
||||
|
||||
aig_j++;
|
||||
aig_latchin.push_back(bit_d1);
|
||||
aig_latchin.push_back(bit_d2);
|
||||
aig_outputs.push_back(mkgate(bit_q1, bit_q2^1));
|
||||
}
|
||||
|
||||
for (auto it : fairness)
|
||||
{
|
||||
int latch_m = ++fair_live_latches_m;
|
||||
|
||||
log_assert(GetSize(aig_latchin) == fair_live_latches_l);
|
||||
fair_live_latches_l += 1;
|
||||
|
||||
int bit_a = bit2aig(it.first);
|
||||
int bit_en = bit2aig(it.second);
|
||||
int bit_q = 2*latch_m;
|
||||
|
||||
aig_f++;
|
||||
aig_latchin.push_back(mkgate(mkgate(bit_q^1, bit_en^1)^1, bit_a^1));
|
||||
aig_outputs.push_back(bit_q^1);
|
||||
}
|
||||
}
|
||||
|
||||
void write_aiger(std::ostream &f, bool ascii_mode, bool miter_mode, bool symbols_mode)
|
||||
{
|
||||
int aig_obc = aig_o + aig_b + aig_c;
|
||||
int aig_obcj = aig_obc + aig_j;
|
||||
int aig_obcjf = aig_obcj + aig_f;
|
||||
|
||||
log_assert(aig_m == aig_i + aig_l + aig_a);
|
||||
log_assert(aig_l == GetSize(aig_latchin));
|
||||
log_assert(aig_l == GetSize(aig_latchinit));
|
||||
log_assert(aig_obcjf == GetSize(aig_outputs));
|
||||
|
||||
if (miter_mode) {
|
||||
if (aig_b || aig_c || aig_j || aig_f)
|
||||
log_error("Running AIGER back-end in -miter mode, but design contains $assert, $assume, $live and/or $fair cells!\n");
|
||||
f << stringf("%s %d %d %d 0 %d %d\n", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_a, aig_o);
|
||||
} else {
|
||||
f << stringf("%s %d %d %d %d %d", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_o, aig_a);
|
||||
if (aig_b || aig_c || aig_j || aig_f)
|
||||
f << stringf(" %d %d %d %d", aig_b, aig_c, aig_j, aig_f);
|
||||
f << stringf("\n");
|
||||
}
|
||||
|
||||
if (ascii_mode)
|
||||
{
|
||||
for (int i = 0; i < aig_i; i++)
|
||||
f << stringf("%d\n", 2*i+2);
|
||||
|
||||
for (int i = 0; i < aig_l; i++) {
|
||||
if (zinit_mode || aig_latchinit.at(i) == 0)
|
||||
f << stringf("%d %d\n", 2*(aig_i+i)+2, aig_latchin.at(i));
|
||||
else if (aig_latchinit.at(i) == 1)
|
||||
f << stringf("%d %d 1\n", 2*(aig_i+i)+2, aig_latchin.at(i));
|
||||
else if (aig_latchinit.at(i) == 2)
|
||||
f << stringf("%d %d %d\n", 2*(aig_i+i)+2, aig_latchin.at(i), 2*(aig_i+i)+2);
|
||||
}
|
||||
|
||||
for (int i = 0; i < aig_obc; i++)
|
||||
f << stringf("%d\n", aig_outputs.at(i));
|
||||
|
||||
for (int i = aig_obc; i < aig_obcj; i++)
|
||||
f << stringf("1\n");
|
||||
|
||||
for (int i = aig_obc; i < aig_obcj; i++)
|
||||
f << stringf("%d\n", aig_outputs.at(i));
|
||||
|
||||
for (int i = aig_obcj; i < aig_obcjf; i++)
|
||||
f << stringf("%d\n", aig_outputs.at(i));
|
||||
|
||||
for (int i = 0; i < aig_a; i++)
|
||||
f << stringf("%d %d %d\n", 2*(aig_i+aig_l+i)+2, aig_gates.at(i).first, aig_gates.at(i).second);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < aig_l; i++) {
|
||||
if (zinit_mode || aig_latchinit.at(i) == 0)
|
||||
f << stringf("%d\n", aig_latchin.at(i));
|
||||
else if (aig_latchinit.at(i) == 1)
|
||||
f << stringf("%d 1\n", aig_latchin.at(i));
|
||||
else if (aig_latchinit.at(i) == 2)
|
||||
f << stringf("%d %d\n", aig_latchin.at(i), 2*(aig_i+i)+2);
|
||||
}
|
||||
|
||||
for (int i = 0; i < aig_obc; i++)
|
||||
f << stringf("%d\n", aig_outputs.at(i));
|
||||
|
||||
for (int i = aig_obc; i < aig_obcj; i++)
|
||||
f << stringf("1\n");
|
||||
|
||||
for (int i = aig_obc; i < aig_obcj; i++)
|
||||
f << stringf("%d\n", aig_outputs.at(i));
|
||||
|
||||
for (int i = aig_obcj; i < aig_obcjf; i++)
|
||||
f << stringf("%d\n", aig_outputs.at(i));
|
||||
|
||||
for (int i = 0; i < aig_a; i++) {
|
||||
int lhs = 2*(aig_i+aig_l+i)+2;
|
||||
int rhs0 = aig_gates.at(i).first;
|
||||
int rhs1 = aig_gates.at(i).second;
|
||||
int delta0 = lhs - rhs0;
|
||||
int delta1 = rhs0 - rhs1;
|
||||
aiger_encode(f, delta0);
|
||||
aiger_encode(f, delta1);
|
||||
}
|
||||
}
|
||||
|
||||
if (symbols_mode)
|
||||
{
|
||||
dict<string, vector<string>> symbols;
|
||||
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
if (wire->name[0] == '$')
|
||||
continue;
|
||||
|
||||
SigSpec sig = sigmap(wire);
|
||||
|
||||
for (int i = 0; i < GetSize(wire); i++)
|
||||
{
|
||||
if (sig[i].wire == nullptr) {
|
||||
if (wire->port_output)
|
||||
sig[i] = SigBit(wire, i);
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wire->port_input) {
|
||||
int a = aig_map.at(sig[i]);
|
||||
log_assert((a & 1) == 0);
|
||||
if (GetSize(wire) != 1)
|
||||
symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s[%d]", log_id(wire), i));
|
||||
else
|
||||
symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s", log_id(wire)));
|
||||
}
|
||||
|
||||
if (wire->port_output) {
|
||||
int o = ordered_outputs.at(SigSpec(wire, i));
|
||||
if (GetSize(wire) != 1)
|
||||
symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s[%d]", log_id(wire), i));
|
||||
else
|
||||
symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s", log_id(wire)));
|
||||
}
|
||||
|
||||
if (init_inputs.count(sig[i])) {
|
||||
int a = init_inputs.at(sig[i]);
|
||||
log_assert((a & 1) == 0);
|
||||
if (GetSize(wire) != 1)
|
||||
symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s[%d]", log_id(wire), i));
|
||||
else
|
||||
symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s", log_id(wire)));
|
||||
}
|
||||
|
||||
if (ordered_latches.count(sig[i])) {
|
||||
int l = ordered_latches.at(sig[i]);
|
||||
const char *p = (zinit_mode && (aig_latchinit.at(l) == 1)) ? "!" : "";
|
||||
if (GetSize(wire) != 1)
|
||||
symbols[stringf("l%d", l)].push_back(stringf("%s%s[%d]", p, log_id(wire), i));
|
||||
else
|
||||
symbols[stringf("l%d", l)].push_back(stringf("%s%s", p, log_id(wire)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
symbols.sort();
|
||||
|
||||
for (auto &sym : symbols) {
|
||||
f << sym.first;
|
||||
std::sort(sym.second.begin(), sym.second.end());
|
||||
for (auto &s : sym.second)
|
||||
f << " " << s;
|
||||
f << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
f << stringf("c\nGenerated by %s\n", yosys_version_str);
|
||||
}
|
||||
|
||||
void write_map(std::ostream &f, bool verbose_map)
|
||||
{
|
||||
dict<int, string> input_lines;
|
||||
dict<int, string> init_lines;
|
||||
dict<int, string> output_lines;
|
||||
dict<int, string> latch_lines;
|
||||
dict<int, string> wire_lines;
|
||||
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
if (!verbose_map && wire->name[0] == '$')
|
||||
continue;
|
||||
|
||||
SigSpec sig = sigmap(wire);
|
||||
|
||||
for (int i = 0; i < GetSize(wire); i++)
|
||||
{
|
||||
if (aig_map.count(sig[i]) == 0 || sig[i].wire == nullptr)
|
||||
continue;
|
||||
|
||||
int a = aig_map.at(sig[i]);
|
||||
|
||||
if (verbose_map)
|
||||
wire_lines[a] += stringf("wire %d %d %s\n", a, i, log_id(wire));
|
||||
|
||||
if (wire->port_input) {
|
||||
log_assert((a & 1) == 0);
|
||||
input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, i, log_id(wire));
|
||||
}
|
||||
|
||||
if (wire->port_output) {
|
||||
int o = ordered_outputs.at(sig[i]);
|
||||
output_lines[o] += stringf("output %d %d %s\n", o, i, log_id(wire));
|
||||
}
|
||||
|
||||
if (init_inputs.count(sig[i])) {
|
||||
int a = init_inputs.at(sig[i]);
|
||||
log_assert((a & 1) == 0);
|
||||
init_lines[a] += stringf("init %d %d %s\n", (a >> 1)-1, i, log_id(wire));
|
||||
}
|
||||
|
||||
if (ordered_latches.count(sig[i])) {
|
||||
int l = ordered_latches.at(sig[i]);
|
||||
if (zinit_mode && (aig_latchinit.at(l) == 1))
|
||||
latch_lines[l] += stringf("invlatch %d %d %s\n", l, i, log_id(wire));
|
||||
else
|
||||
latch_lines[l] += stringf("latch %d %d %s\n", l, i, log_id(wire));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input_lines.sort();
|
||||
for (auto &it : input_lines)
|
||||
f << it.second;
|
||||
|
||||
init_lines.sort();
|
||||
for (auto &it : init_lines)
|
||||
f << it.second;
|
||||
|
||||
output_lines.sort();
|
||||
for (auto &it : output_lines)
|
||||
f << it.second;
|
||||
|
||||
latch_lines.sort();
|
||||
for (auto &it : latch_lines)
|
||||
f << it.second;
|
||||
|
||||
wire_lines.sort();
|
||||
for (auto &it : wire_lines)
|
||||
f << it.second;
|
||||
}
|
||||
};
|
||||
|
||||
struct AigerBackend : public Backend {
|
||||
AigerBackend() : Backend("aiger", "write design to AIGER file") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" write_aiger [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Write the current design to an AIGER file. The design must be flattened and\n");
|
||||
log("must not contain any cell types except $_AND_, $_NOT_, simple FF types,\n");
|
||||
log("$assert and $assume cells, and $initstate cells.\n");
|
||||
log("\n");
|
||||
log("$assert and $assume cells are converted to AIGER bad state properties and\n");
|
||||
log("invariant constraints.\n");
|
||||
log("\n");
|
||||
log(" -ascii\n");
|
||||
log(" write ASCII version of AGIER format\n");
|
||||
log("\n");
|
||||
log(" -zinit\n");
|
||||
log(" convert FFs to zero-initialized FFs, adding additional inputs for\n");
|
||||
log(" uninitialized FFs.\n");
|
||||
log("\n");
|
||||
log(" -miter\n");
|
||||
log(" design outputs are AIGER bad state properties\n");
|
||||
log("\n");
|
||||
log(" -symbols\n");
|
||||
log(" include a symbol table in the generated AIGER file\n");
|
||||
log("\n");
|
||||
log(" -map <filename>\n");
|
||||
log(" write an extra file with port and latch symbols\n");
|
||||
log("\n");
|
||||
log(" -vmap <filename>\n");
|
||||
log(" like -map, but more verbose\n");
|
||||
log("\n");
|
||||
log(" -I, -O, -B\n");
|
||||
log(" If the design contains no input/output/assert then create one\n");
|
||||
log(" dummy input/output/bad_state pin to make the tools reading the\n");
|
||||
log(" AIGER file happy.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool ascii_mode = false;
|
||||
bool zinit_mode = false;
|
||||
bool miter_mode = false;
|
||||
bool symbols_mode = false;
|
||||
bool verbose_map = false;
|
||||
bool imode = false;
|
||||
bool omode = false;
|
||||
bool bmode = false;
|
||||
std::string map_filename;
|
||||
|
||||
log_header(design, "Executing AIGER backend.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-ascii") {
|
||||
ascii_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-zinit") {
|
||||
zinit_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-miter") {
|
||||
miter_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-symbols") {
|
||||
symbols_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (map_filename.empty() && args[argidx] == "-map" && argidx+1 < args.size()) {
|
||||
map_filename = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (map_filename.empty() && args[argidx] == "-vmap" && argidx+1 < args.size()) {
|
||||
map_filename = args[++argidx];
|
||||
verbose_map = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-I") {
|
||||
imode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-O") {
|
||||
omode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-B") {
|
||||
bmode = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
Module *top_module = design->top_module();
|
||||
|
||||
if (top_module == nullptr)
|
||||
log_error("Can't find top module in current design!\n");
|
||||
|
||||
AigerWriter writer(top_module, zinit_mode, imode, omode, bmode);
|
||||
writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode);
|
||||
|
||||
if (!map_filename.empty()) {
|
||||
std::ofstream mapf;
|
||||
mapf.open(map_filename.c_str(), std::ofstream::trunc);
|
||||
if (mapf.fail())
|
||||
log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno));
|
||||
writer.write_map(mapf, verbose_map);
|
||||
}
|
||||
}
|
||||
} AigerBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -38,8 +38,10 @@ struct BlifDumperConfig
|
|||
bool impltf_mode;
|
||||
bool gates_mode;
|
||||
bool cname_mode;
|
||||
bool iname_mode;
|
||||
bool param_mode;
|
||||
bool attr_mode;
|
||||
bool iattr_mode;
|
||||
bool blackbox_mode;
|
||||
bool noalias_mode;
|
||||
|
||||
|
@ -48,7 +50,8 @@ struct BlifDumperConfig
|
|||
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),
|
||||
cname_mode(false), param_mode(false), attr_mode(false), blackbox_mode(false), noalias_mode(false) { }
|
||||
cname_mode(false), iname_mode(false), param_mode(false), attr_mode(false), iattr_mode(false),
|
||||
blackbox_mode(false), noalias_mode(false) { }
|
||||
};
|
||||
|
||||
struct BlifDumper
|
||||
|
@ -112,7 +115,7 @@ struct BlifDumper
|
|||
str[i] = '?';
|
||||
|
||||
if (sig.wire->width != 1)
|
||||
str += stringf("[%d]", sig.offset);
|
||||
str += stringf("[%d]", sig.wire->upto ? sig.wire->start_offset+sig.wire->width-sig.offset-1 : sig.wire->start_offset+sig.offset);
|
||||
|
||||
cstr_buf.push_back(str);
|
||||
return cstr_buf.back().c_str();
|
||||
|
@ -240,106 +243,118 @@ struct BlifDumper
|
|||
if (!config->icells_mode && cell->type == "$_NOT_") {
|
||||
f << stringf(".names %s %s\n0 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_AND_") {
|
||||
f << stringf(".names %s %s %s\n11 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_OR_") {
|
||||
f << stringf(".names %s %s %s\n1- 1\n-1 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_XOR_") {
|
||||
f << stringf(".names %s %s %s\n10 1\n01 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_NAND_") {
|
||||
f << stringf(".names %s %s %s\n0- 1\n-0 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_NOR_") {
|
||||
f << stringf(".names %s %s %s\n00 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_XNOR_") {
|
||||
f << stringf(".names %s %s %s\n11 1\n00 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_ANDNOT_") {
|
||||
f << stringf(".names %s %s %s\n10 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_ORNOT_") {
|
||||
f << stringf(".names %s %s %s\n1- 1\n-0 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_AOI3_") {
|
||||
f << stringf(".names %s %s %s %s\n-00 1\n0-0 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_OAI3_") {
|
||||
f << stringf(".names %s %s %s %s\n00- 1\n--0 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_AOI4_") {
|
||||
f << stringf(".names %s %s %s %s %s\n-0-0 1\n-00- 1\n0--0 1\n0-0- 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
|
||||
cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_OAI4_") {
|
||||
f << stringf(".names %s %s %s %s %s\n00-- 1\n--00 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
|
||||
cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_MUX_") {
|
||||
f << stringf(".names %s %s %s %s\n1-0 1\n-11 1\n",
|
||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
|
||||
cstr(cell->getPort("\\S")), cstr(cell->getPort("\\Y")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_FF_") {
|
||||
f << stringf(".latch %s %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
|
||||
cstr_init(cell->getPort("\\Q")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_DFF_N_") {
|
||||
f << stringf(".latch %s %s fe %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
|
||||
cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_DFF_P_") {
|
||||
f << stringf(".latch %s %s re %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
|
||||
cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_DLATCH_N_") {
|
||||
f << stringf(".latch %s %s al %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
|
||||
cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$_DLATCH_P_") {
|
||||
f << stringf(".latch %s %s ah %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
|
||||
cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q")));
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$lut") {
|
||||
|
@ -361,7 +376,7 @@ struct BlifDumper
|
|||
}
|
||||
f << " 1\n";
|
||||
}
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
if (!config->icells_mode && cell->type == "$sop") {
|
||||
|
@ -389,7 +404,7 @@ struct BlifDumper
|
|||
}
|
||||
f << " 1\n";
|
||||
}
|
||||
continue;
|
||||
goto internal_cell;
|
||||
}
|
||||
|
||||
f << stringf(".%s %s", subckt_or_gate(cell->type.str()), cstr(cell->type));
|
||||
|
@ -409,6 +424,14 @@ struct BlifDumper
|
|||
dump_params(".attr", cell->attributes);
|
||||
if (config->param_mode)
|
||||
dump_params(".param", cell->parameters);
|
||||
|
||||
if (0) {
|
||||
internal_cell:
|
||||
if (config->iname_mode)
|
||||
f << stringf(".cname %s\n", cstr(cell->name));
|
||||
if (config->iattr_mode)
|
||||
dump_params(".attr", cell->attributes);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &conn : module->connections())
|
||||
|
@ -441,7 +464,7 @@ struct BlifDumper
|
|||
|
||||
struct BlifBackend : public Backend {
|
||||
BlifBackend() : Backend("blif", "write design to BLIF file") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -499,6 +522,11 @@ struct BlifBackend : public Backend {
|
|||
log(" -cname\n");
|
||||
log(" use the non-standard .cname statement to write cell names\n");
|
||||
log("\n");
|
||||
log(" -iname, -iattr\n");
|
||||
log(" enable -cname and -attr functionality for .names statements\n");
|
||||
log(" (the .cname and .attr statements will be included in the BLIF\n");
|
||||
log(" output after the truth table for the .names statement)\n");
|
||||
log("\n");
|
||||
log(" -blackbox\n");
|
||||
log(" write blackbox cells with .blackbox statement.\n");
|
||||
log("\n");
|
||||
|
@ -506,7 +534,7 @@ struct BlifBackend : public Backend {
|
|||
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<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::string top_module_name;
|
||||
std::string buf_type, buf_in, buf_out;
|
||||
|
@ -575,6 +603,14 @@ struct BlifBackend : public Backend {
|
|||
config.attr_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-iname") {
|
||||
config.iname_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-iattr") {
|
||||
config.iattr_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-blackbox") {
|
||||
config.blackbox_mode = true;
|
||||
continue;
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
|
||||
This is the Yosys BTOR backend.
|
||||
It is developed by Ahmed Irfan <irfan@fbk.eu> - Fondazione Bruno Kessler, Trento, Italy
|
||||
|
||||
Master git repository for the BTOR backend:
|
||||
https://github.com/ahmedirfan1983/yosys
|
||||
|
||||
|
||||
[[CITE]] BTOR: Bit-Precise Modelling of Word-Level Problems for Model Checking
|
||||
Johannes Kepler University, Linz, Austria
|
||||
http://fmv.jku.at/papers/BrummayerBiereLonsing-BPR08.pdf
|
||||
|
||||
|
||||
Todos:
|
||||
------
|
||||
|
||||
- Add checks for unsupported stuff
|
||||
- unsupported cell types
|
||||
- async resets
|
||||
- etc..
|
||||
|
||||
- Add support for $lut cells
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,30 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
rm -rf test_cells.tmp
|
||||
mkdir -p test_cells.tmp
|
||||
cd test_cells.tmp
|
||||
|
||||
../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod'
|
||||
|
||||
for fn in test_*.il; do
|
||||
../../../yosys -p "
|
||||
read_ilang $fn
|
||||
rename gold gate
|
||||
synth
|
||||
|
||||
read_ilang $fn
|
||||
miter -equiv -make_assert -flatten gold gate main
|
||||
hierarchy -top main
|
||||
write_btor ${fn%.il}.btor
|
||||
"
|
||||
boolectormc -kmax 1 --trace-gen --stop-first -v ${fn%.il}.btor > ${fn%.il}.out
|
||||
if grep " SATISFIABLE" ${fn%.il}.out; then
|
||||
echo "Check failed for ${fn%.il}."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "OK."
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Script to write BTOR from Verilog design
|
||||
#
|
||||
|
||||
if [ "$#" -ne 3 ]; then
|
||||
echo "Usage: $0 input.v output.btor top-module-name" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! [ -e "$1" ]; then
|
||||
echo "$1 not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FULL_PATH=$(readlink -f $1)
|
||||
DIR=$(dirname $FULL_PATH)
|
||||
|
||||
./yosys -q -p "
|
||||
read_verilog -sv $1;
|
||||
hierarchy -top $3;
|
||||
hierarchy -libdir $DIR;
|
||||
hierarchy -check;
|
||||
proc;
|
||||
opt; opt_expr -mux_undef; opt;
|
||||
rename -hide;;;
|
||||
#techmap -map +/pmux2mux.v;;
|
||||
splice; opt;
|
||||
memory_dff -wr_only;
|
||||
memory_collect;;
|
||||
flatten;;
|
||||
memory_unpack;
|
||||
splitnets -driver;
|
||||
setundef -zero -undriven;
|
||||
opt;;;
|
||||
write_btor $2;"
|
||||
|
|
@ -31,22 +31,24 @@ USING_YOSYS_NAMESPACE
|
|||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#define EDIF_DEF(_id) edif_names(RTLIL::unescape_id(_id), true).c_str()
|
||||
#define EDIF_DEFR(_id, _ren, _bl, _br) edif_names(RTLIL::unescape_id(_id), true, _ren, _bl, _br).c_str()
|
||||
#define EDIF_REF(_id) edif_names(RTLIL::unescape_id(_id), false).c_str()
|
||||
|
||||
namespace
|
||||
{
|
||||
struct EdifNames
|
||||
{
|
||||
int counter;
|
||||
char delim_left, delim_right;
|
||||
std::set<std::string> generated_names, used_names;
|
||||
std::map<std::string, std::string> name_map;
|
||||
|
||||
EdifNames() : counter(1) { }
|
||||
EdifNames() : counter(1), delim_left('['), delim_right(']') { }
|
||||
|
||||
std::string operator()(std::string id, bool define)
|
||||
std::string operator()(std::string id, bool define, bool port_rename = false, int range_left = 0, int range_right = 0)
|
||||
{
|
||||
if (define) {
|
||||
std::string new_id = operator()(id, false);
|
||||
if (port_rename)
|
||||
return stringf("(rename %s \"%s%c%d:%d%c\")", new_id.c_str(), id.c_str(), delim_left, range_left, range_right, delim_right);
|
||||
return new_id != id ? stringf("(rename %s \"%s\")", new_id.c_str(), id.c_str()) : id;
|
||||
}
|
||||
|
||||
|
@ -85,11 +87,10 @@ namespace
|
|||
return gen_name;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct EdifBackend : public Backend {
|
||||
EdifBackend() : Backend("edif", "write design to EDIF netlist file") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -105,17 +106,25 @@ struct EdifBackend : public Backend {
|
|||
log(" if the design contains constant nets. use \"hilomap\" to map to custom\n");
|
||||
log(" constant drivers first)\n");
|
||||
log("\n");
|
||||
log(" -attrprop\n");
|
||||
log(" create EDIF properties for cell attributes\n");
|
||||
log("\n");
|
||||
log(" -pvector {par|bra|ang}\n");
|
||||
log(" sets the delimiting character for module port rename clauses to\n");
|
||||
log(" parentheses, square brackets, or angle brackets.\n");
|
||||
log("\n");
|
||||
log("Unfortunately there are different \"flavors\" of the EDIF file format. This\n");
|
||||
log("command generates EDIF files for the Xilinx place&route tools. It might be\n");
|
||||
log("necessary to make small modifications to this command when a different tool\n");
|
||||
log("is targeted.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing EDIF backend.\n");
|
||||
|
||||
std::string top_module_name;
|
||||
bool port_rename = false;
|
||||
bool attr_properties = false;
|
||||
std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports;
|
||||
bool nogndvcc = false;
|
||||
CellTypes ct(design);
|
||||
|
@ -132,6 +141,23 @@ struct EdifBackend : public Backend {
|
|||
nogndvcc = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-attrprop") {
|
||||
attr_properties = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-pvector" && argidx+1 < args.size()) {
|
||||
std::string parray;
|
||||
port_rename = true;
|
||||
parray = args[++argidx];
|
||||
if (parray == "par") {
|
||||
edif_names.delim_left = '(';edif_names.delim_right = ')';
|
||||
} else if (parray == "ang") {
|
||||
edif_names.delim_left = '<';edif_names.delim_right = '>';
|
||||
} else {
|
||||
edif_names.delim_left = '[';edif_names.delim_right = ']';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
@ -214,8 +240,18 @@ struct EdifBackend : public Backend {
|
|||
}
|
||||
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);
|
||||
else {
|
||||
int b[2] = {port_it.second-1, 0};
|
||||
auto m = design->module(cell_it.first);
|
||||
if (m) {
|
||||
auto w = m->wire(port_it.first);
|
||||
if (w) {
|
||||
b[w->upto ? 0 : 1] = w->start_offset;
|
||||
b[w->upto ? 1 : 0] = w->start_offset+GetSize(w)-1;
|
||||
}
|
||||
}
|
||||
*f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEFR(port_it.first, port_rename, b[0], b[1]), port_it.second, dir);
|
||||
}
|
||||
}
|
||||
*f << stringf(" )\n");
|
||||
*f << stringf(" )\n");
|
||||
|
@ -283,10 +319,13 @@ struct EdifBackend : public Backend {
|
|||
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire));
|
||||
net_join_db[sig].insert(stringf("(portRef %s)", EDIF_REF(wire->name)));
|
||||
} else {
|
||||
*f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEF(wire->name), wire->width, dir);
|
||||
int b[2];
|
||||
b[wire->upto ? 0 : 1] = wire->start_offset;
|
||||
b[wire->upto ? 1 : 0] = wire->start_offset + GetSize(wire) - 1;
|
||||
*f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEFR(wire->name, port_rename, b[0], b[1]), wire->width, dir);
|
||||
for (int i = 0; i < wire->width; i++) {
|
||||
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i));
|
||||
net_join_db[sig].insert(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), i));
|
||||
net_join_db[sig].insert(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -301,42 +340,79 @@ struct EdifBackend : public Backend {
|
|||
*f << stringf(" (instance %s\n", EDIF_DEF(cell->name));
|
||||
*f << stringf(" (viewRef VIEW_NETLIST (cellRef %s%s))", EDIF_REF(cell->type),
|
||||
lib_cell_ports.count(cell->type) > 0 ? " (libraryRef LIB)" : "");
|
||||
for (auto &p : cell->parameters)
|
||||
if ((p.second.flags & RTLIL::CONST_FLAG_STRING) != 0)
|
||||
*f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(p.first), p.second.decode_string().c_str());
|
||||
else if (p.second.bits.size() <= 32 && RTLIL::SigSpec(p.second).is_fully_def())
|
||||
*f << stringf("\n (property %s (integer %u))", EDIF_DEF(p.first), p.second.as_int());
|
||||
|
||||
auto add_prop = [&](IdString name, Const val) {
|
||||
if ((val.flags & RTLIL::CONST_FLAG_STRING) != 0)
|
||||
*f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(name), val.decode_string().c_str());
|
||||
else if (val.bits.size() <= 32 && RTLIL::SigSpec(val).is_fully_def())
|
||||
*f << stringf("\n (property %s (integer %u))", EDIF_DEF(name), val.as_int());
|
||||
else {
|
||||
std::string hex_string = "";
|
||||
for (size_t i = 0; i < p.second.bits.size(); i += 4) {
|
||||
for (size_t i = 0; i < val.bits.size(); i += 4) {
|
||||
int digit_value = 0;
|
||||
if (i+0 < p.second.bits.size() && p.second.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1;
|
||||
if (i+1 < p.second.bits.size() && p.second.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2;
|
||||
if (i+2 < p.second.bits.size() && p.second.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4;
|
||||
if (i+3 < p.second.bits.size() && p.second.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8;
|
||||
if (i+0 < val.bits.size() && val.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1;
|
||||
if (i+1 < val.bits.size() && val.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2;
|
||||
if (i+2 < val.bits.size() && val.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4;
|
||||
if (i+3 < val.bits.size() && val.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8;
|
||||
char digit_str[2] = { "0123456789abcdef"[digit_value], 0 };
|
||||
hex_string = std::string(digit_str) + hex_string;
|
||||
}
|
||||
*f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(p.first), GetSize(p.second.bits), hex_string.c_str());
|
||||
*f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(name), GetSize(val.bits), hex_string.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
for (auto &p : cell->parameters)
|
||||
add_prop(p.first, p.second);
|
||||
if (attr_properties)
|
||||
for (auto &p : cell->attributes)
|
||||
add_prop(p.first, p.second);
|
||||
|
||||
*f << stringf(")\n");
|
||||
for (auto &p : cell->connections()) {
|
||||
RTLIL::SigSpec sig = sigmap(p.second);
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
if (sig.size() == 1)
|
||||
if (sig[i].wire == NULL && sig[i] != RTLIL::State::S0 && sig[i] != RTLIL::State::S1)
|
||||
log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n",
|
||||
i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i]));
|
||||
else if (sig.size() == 1)
|
||||
net_join_db[sig[i]].insert(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)));
|
||||
else
|
||||
net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))", EDIF_REF(p.first), i, EDIF_REF(cell->name)));
|
||||
else {
|
||||
int member_idx = GetSize(sig)-i-1;
|
||||
auto m = design->module(cell->type);
|
||||
if (m) {
|
||||
auto w = m->wire(p.first);
|
||||
if (w)
|
||||
member_idx = GetSize(w)-i-1;
|
||||
}
|
||||
net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))",
|
||||
EDIF_REF(p.first), member_idx, EDIF_REF(cell->name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &it : net_join_db) {
|
||||
RTLIL::SigBit sig = it.first;
|
||||
if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1)
|
||||
continue;
|
||||
std::string netname = log_signal(sig);
|
||||
if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1) {
|
||||
if (sig == RTLIL::State::Sx) {
|
||||
for (auto &ref : it.second)
|
||||
log_warning("Exporting x-bit on %s as zero bit.\n", ref.c_str());
|
||||
sig = RTLIL::State::S0;
|
||||
} else {
|
||||
for (auto &ref : it.second)
|
||||
log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.c_str());
|
||||
log_abort();
|
||||
}
|
||||
}
|
||||
std::string netname;
|
||||
if (sig == RTLIL::State::S0)
|
||||
netname = "GND_NET";
|
||||
else if (sig == RTLIL::State::S1)
|
||||
netname = "VCC_NET";
|
||||
else {
|
||||
netname = log_signal(sig);
|
||||
for (size_t i = 0; i < netname.size(); i++)
|
||||
if (netname[i] == ' ' || netname[i] == '\\')
|
||||
netname.erase(netname.begin() + i--);
|
||||
}
|
||||
*f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
|
||||
for (auto &ref : it.second)
|
||||
*f << stringf(" %s\n", ref.c_str());
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import numpy as np
|
||||
|
||||
enable_upto = True
|
||||
enable_offset = True
|
||||
enable_hierarchy = True
|
||||
enable_logic = True
|
||||
|
||||
def make_module(f, modname, width, subs):
|
||||
print("module %s (A, B, C, X, Y, Z);" % modname, file=f)
|
||||
inbits = list()
|
||||
outbits = list()
|
||||
|
||||
for p in "ABC":
|
||||
offset = np.random.randint(10) if enable_offset else 0
|
||||
if enable_upto and np.random.randint(2):
|
||||
print(" input [%d:%d] %s;" % (offset, offset+width-1, p), file=f)
|
||||
else:
|
||||
print(" input [%d:%d] %s;" % (offset+width-1, offset, p), file=f)
|
||||
for i in range(offset, offset+width):
|
||||
inbits.append("%s[%d]" % (p, i))
|
||||
|
||||
for p in "XYZ":
|
||||
offset = np.random.randint(10) if enable_offset else 0
|
||||
if enable_upto and np.random.randint(2):
|
||||
print(" output [%d:%d] %s;" % (offset, offset+width-1, p), file=f)
|
||||
else:
|
||||
print(" output [%d:%d] %s;" % (offset+width-1, offset, p), file=f)
|
||||
for i in range(offset, offset+width):
|
||||
outbits.append("%s[%d]" % (p, i))
|
||||
|
||||
instidx = 0
|
||||
subcandidates = list(subs.keys())
|
||||
|
||||
while len(outbits) > 0:
|
||||
submod = None
|
||||
if len(subcandidates):
|
||||
submod = np.random.choice(subcandidates)
|
||||
subcandidates.remove(submod)
|
||||
|
||||
if submod is None or 3*subs[submod] >= len(outbits):
|
||||
for bit in outbits:
|
||||
if enable_logic:
|
||||
print(" assign %s = %s & ~%s;" % (bit, np.random.choice(inbits), np.random.choice(inbits)), file=f)
|
||||
else:
|
||||
print(" assign %s = %s;" % (bit, np.random.choice(inbits)), file=f)
|
||||
break
|
||||
|
||||
instidx += 1
|
||||
print(" %s inst%d (" % (submod, instidx), file=f)
|
||||
|
||||
for p in "ABC":
|
||||
print(" .%s({%s})," % (p, ",".join(np.random.choice(inbits, subs[submod]))), file=f)
|
||||
|
||||
for p in "XYZ":
|
||||
bits = list(np.random.choice(outbits, subs[submod], False))
|
||||
for bit in bits:
|
||||
outbits.remove(bit)
|
||||
print(" .%s({%s})%s" % (p, ",".join(bits), "," if p != "Z" else ""), file=f)
|
||||
|
||||
print(" );", file=f);
|
||||
|
||||
print("endmodule", file=f)
|
||||
|
||||
with open("test_top.v", "w") as f:
|
||||
if enable_hierarchy:
|
||||
make_module(f, "sub1", 2, {})
|
||||
make_module(f, "sub2", 3, {})
|
||||
make_module(f, "sub3", 4, {})
|
||||
make_module(f, "sub4", 8, {"sub1": 2, "sub2": 3, "sub3": 4})
|
||||
make_module(f, "sub5", 8, {"sub1": 2, "sub2": 3, "sub3": 4})
|
||||
make_module(f, "sub6", 8, {"sub1": 2, "sub2": 3, "sub3": 4})
|
||||
make_module(f, "top", 32, {"sub4": 8, "sub5": 8, "sub6": 8})
|
||||
else:
|
||||
make_module(f, "top", 32, {})
|
||||
|
||||
os.system("set -x; ../../yosys -p 'synth_xilinx -top top; write_edif -pvector par test_syn.edif' test_top.v")
|
||||
|
||||
with open("test_syn.tcl", "w") as f:
|
||||
print("read_edif test_syn.edif", file=f)
|
||||
print("link_design", file=f)
|
||||
print("write_verilog -force test_syn.v", file=f)
|
||||
|
||||
os.system("set -x; vivado -nojournal -nolog -mode batch -source test_syn.tcl")
|
||||
|
||||
with open("test_tb.v", "w") as f:
|
||||
print("module tb;", file=f)
|
||||
print(" reg [31:0] A, B, C;", file=f)
|
||||
print(" wire [31:0] X, Y, Z;", file=f)
|
||||
print("", file=f)
|
||||
print(" top uut (", file=f)
|
||||
print(" .A(A),", file=f)
|
||||
print(" .B(B),", file=f)
|
||||
print(" .C(C),", file=f)
|
||||
print(" .X(X),", file=f)
|
||||
print(" .Y(Y),", file=f)
|
||||
print(" .Z(Z)", file=f)
|
||||
print(" );", file=f)
|
||||
print("", file=f)
|
||||
print(" initial begin", file=f)
|
||||
for i in range(100):
|
||||
print(" A = 32'h%08x;" % np.random.randint(2**32), file=f)
|
||||
print(" B = 32'h%08x;" % np.random.randint(2**32), file=f)
|
||||
print(" C = 32'h%08x;" % np.random.randint(2**32), file=f)
|
||||
print(" #10;", file=f)
|
||||
print(" $display(\"%x %x %x\", X, Y, Z);", file=f)
|
||||
print(" #10;", file=f)
|
||||
print(" $finish;", file=f)
|
||||
print(" end", file=f)
|
||||
print("endmodule", file=f)
|
||||
|
||||
os.system("set -x; iverilog -o test_gold test_tb.v test_top.v")
|
||||
os.system("set -x; iverilog -o test_gate test_tb.v test_syn.v ../../techlibs/xilinx/cells_sim.v")
|
||||
|
||||
os.system("set -x; ./test_gold > test_gold.out")
|
||||
os.system("set -x; ./test_gate > test_gate.out")
|
||||
|
||||
os.system("set -x; md5sum test_gold.out test_gate.out")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
test.fir
|
||||
test_out.v
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/firrtl/firrtl.o
|
||||
|
|
@ -0,0 +1,677 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/cellaigs.h"
|
||||
#include "kernel/log.h"
|
||||
#include <string>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
pool<string> used_names;
|
||||
dict<IdString, string> namecache;
|
||||
int autoid_counter;
|
||||
|
||||
typedef unsigned FDirection;
|
||||
static const FDirection FD_NODIRECTION = 0x0;
|
||||
static const FDirection FD_IN = 0x1;
|
||||
static const FDirection FD_OUT = 0x2;
|
||||
static const FDirection FD_INOUT = 0x3;
|
||||
|
||||
// Get a port direction with respect to a specific module.
|
||||
FDirection getPortFDirection(IdString id, Module *module)
|
||||
{
|
||||
Wire *wire = module->wires_.at(id);
|
||||
FDirection direction = FD_NODIRECTION;
|
||||
if (wire && wire->port_id)
|
||||
{
|
||||
if (wire->port_input)
|
||||
direction |= FD_IN;
|
||||
if (wire->port_output)
|
||||
direction |= FD_OUT;
|
||||
}
|
||||
return direction;
|
||||
}
|
||||
|
||||
string next_id()
|
||||
{
|
||||
string new_id;
|
||||
|
||||
while (1) {
|
||||
new_id = stringf("_%d", autoid_counter++);
|
||||
if (used_names.count(new_id) == 0) break;
|
||||
}
|
||||
|
||||
used_names.insert(new_id);
|
||||
return new_id;
|
||||
}
|
||||
|
||||
const char *make_id(IdString id)
|
||||
{
|
||||
if (namecache.count(id) != 0)
|
||||
return namecache.at(id).c_str();
|
||||
|
||||
string new_id = log_id(id);
|
||||
|
||||
for (int i = 0; i < GetSize(new_id); i++)
|
||||
{
|
||||
char &ch = new_id[i];
|
||||
if ('a' <= ch && ch <= 'z') continue;
|
||||
if ('A' <= ch && ch <= 'Z') continue;
|
||||
if ('0' <= ch && ch <= '9' && i != 0) continue;
|
||||
if ('_' == ch) continue;
|
||||
ch = '_';
|
||||
}
|
||||
|
||||
while (used_names.count(new_id) != 0)
|
||||
new_id += '_';
|
||||
|
||||
namecache[id] = new_id;
|
||||
used_names.insert(new_id);
|
||||
return namecache.at(id).c_str();
|
||||
}
|
||||
|
||||
struct FirrtlWorker
|
||||
{
|
||||
Module *module;
|
||||
std::ostream &f;
|
||||
|
||||
dict<SigBit, pair<string, int>> reverse_wire_map;
|
||||
string unconn_id;
|
||||
RTLIL::Design *design;
|
||||
std::string indent;
|
||||
|
||||
void register_reverse_wire_map(string id, SigSpec sig)
|
||||
{
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
reverse_wire_map[sig[i]] = make_pair(id, i);
|
||||
}
|
||||
|
||||
FirrtlWorker(Module *module, std::ostream &f, RTLIL::Design *theDesign) : module(module), f(f), design(theDesign), indent(" ")
|
||||
{
|
||||
}
|
||||
|
||||
string make_expr(const SigSpec &sig)
|
||||
{
|
||||
string expr;
|
||||
|
||||
for (auto chunk : sig.chunks())
|
||||
{
|
||||
string new_expr;
|
||||
|
||||
if (chunk.wire == nullptr)
|
||||
{
|
||||
std::vector<RTLIL::State> bits = chunk.data;
|
||||
new_expr = stringf("UInt<%d>(\"h", GetSize(bits));
|
||||
|
||||
while (GetSize(bits) % 4 != 0)
|
||||
bits.push_back(State::S0);
|
||||
|
||||
for (int i = GetSize(bits)-4; i >= 0; i -= 4)
|
||||
{
|
||||
int val = 0;
|
||||
if (bits[i+0] == State::S1) val += 1;
|
||||
if (bits[i+1] == State::S1) val += 2;
|
||||
if (bits[i+2] == State::S1) val += 4;
|
||||
if (bits[i+3] == State::S1) val += 8;
|
||||
new_expr.push_back(val < 10 ? '0' + val : 'a' + val - 10);
|
||||
}
|
||||
|
||||
new_expr += "\")";
|
||||
}
|
||||
else if (chunk.offset == 0 && chunk.width == chunk.wire->width)
|
||||
{
|
||||
new_expr = make_id(chunk.wire->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
string wire_id = make_id(chunk.wire->name);
|
||||
new_expr = stringf("bits(%s, %d, %d)", wire_id.c_str(), chunk.offset + chunk.width - 1, chunk.offset);
|
||||
}
|
||||
|
||||
if (expr.empty())
|
||||
expr = new_expr;
|
||||
else
|
||||
expr = "cat(" + new_expr + ", " + expr + ")";
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::string fid(RTLIL::IdString internal_id)
|
||||
{
|
||||
const char *str = internal_id.c_str();
|
||||
return *str == '\\' ? str + 1 : str;
|
||||
}
|
||||
|
||||
|
||||
std::string cellname(RTLIL::Cell *cell)
|
||||
{
|
||||
return fid(cell->name).c_str();
|
||||
}
|
||||
|
||||
void process_instance(RTLIL::Cell *cell, vector<string> &wire_exprs)
|
||||
{
|
||||
std::string cell_type = fid(cell->type);
|
||||
|
||||
std::string cell_name = cellname(cell);
|
||||
std::string cell_name_comment;
|
||||
if (cell_name != fid(cell->name))
|
||||
cell_name_comment = " /* " + fid(cell->name) + " */ ";
|
||||
else
|
||||
cell_name_comment = "";
|
||||
// Find the module corresponding to this instance.
|
||||
auto instModule = design->module(cell->type);
|
||||
wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), cell_type.c_str()));
|
||||
|
||||
for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
|
||||
if (it->second.size() > 0) {
|
||||
const SigSpec &secondSig = it->second;
|
||||
const std::string firstName = cell_name + "." + make_id(it->first);
|
||||
const std::string secondName = make_expr(secondSig);
|
||||
// Find the direction for this port.
|
||||
FDirection dir = getPortFDirection(it->first, instModule);
|
||||
std::string source, sink;
|
||||
switch (dir) {
|
||||
case FD_INOUT:
|
||||
log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", log_id(cell_type), log_signal(it->second));
|
||||
case FD_OUT:
|
||||
source = firstName;
|
||||
sink = secondName;
|
||||
break;
|
||||
case FD_NODIRECTION:
|
||||
log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", log_id(cell_type), log_signal(it->second));
|
||||
/* FALL_THROUGH */
|
||||
case FD_IN:
|
||||
source = secondName;
|
||||
sink = firstName;
|
||||
break;
|
||||
default:
|
||||
log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", log_id(cell_type), log_signal(it->second), dir);
|
||||
break;
|
||||
}
|
||||
wire_exprs.push_back(stringf("\n%s%s <= %s", indent.c_str(), sink.c_str(), source.c_str()));
|
||||
}
|
||||
}
|
||||
wire_exprs.push_back(stringf("\n"));
|
||||
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
f << stringf(" module %s:\n", make_id(module->name));
|
||||
vector<string> port_decls, wire_decls, cell_exprs, wire_exprs;
|
||||
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
const auto wireName = make_id(wire->name);
|
||||
if (wire->port_id)
|
||||
{
|
||||
if (wire->port_input && wire->port_output)
|
||||
log_error("Module port %s.%s is inout!\n", log_id(module), log_id(wire));
|
||||
port_decls.push_back(stringf(" %s %s: UInt<%d>\n", wire->port_input ? "input" : "output",
|
||||
wireName, wire->width));
|
||||
}
|
||||
else
|
||||
{
|
||||
wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", wireName, wire->width));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto cell : module->cells())
|
||||
{
|
||||
// Is this cell is a module instance?
|
||||
if (cell->type[0] != '$')
|
||||
{
|
||||
process_instance(cell, wire_exprs);
|
||||
continue;
|
||||
}
|
||||
if (cell->type.in("$not", "$logic_not", "$neg", "$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_bool", "$reduce_xnor"))
|
||||
{
|
||||
string y_id = make_id(cell->name);
|
||||
bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool();
|
||||
int y_width = cell->parameters.at("\\Y_WIDTH").as_int();
|
||||
string a_expr = make_expr(cell->getPort("\\A"));
|
||||
wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
|
||||
|
||||
if (cell->parameters.at("\\A_SIGNED").as_bool()) {
|
||||
a_expr = "asSInt(" + a_expr + ")";
|
||||
}
|
||||
|
||||
// Don't use the results of logical operations (a single bit) to control padding
|
||||
if (!(cell->type.in("$eq", "$eqx", "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$reduce_bool", "$logic_not") && y_width == 1) ) {
|
||||
a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
|
||||
}
|
||||
|
||||
string primop;
|
||||
bool always_uint = false;
|
||||
if (cell->type == "$not") primop = "not";
|
||||
if (cell->type == "$neg") primop = "neg";
|
||||
if (cell->type == "$logic_not") {
|
||||
primop = "eq";
|
||||
a_expr = stringf("%s, UInt(0)", a_expr.c_str());
|
||||
}
|
||||
if (cell->type == "$reduce_and") primop = "andr";
|
||||
if (cell->type == "$reduce_or") primop = "orr";
|
||||
if (cell->type == "$reduce_xor") primop = "xorr";
|
||||
if (cell->type == "$reduce_xnor") {
|
||||
primop = "not";
|
||||
a_expr = stringf("xorr(%s)", a_expr.c_str());
|
||||
}
|
||||
if (cell->type == "$reduce_bool") {
|
||||
primop = "neq";
|
||||
// Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand.
|
||||
bool a_signed = cell->parameters.at("\\A_SIGNED").as_bool();
|
||||
int a_width = cell->parameters.at("\\A_WIDTH").as_int();
|
||||
a_expr = stringf("%s, %cInt<%d>(0)", a_expr.c_str(), a_signed ? 'S' : 'U', a_width);
|
||||
}
|
||||
|
||||
string expr = stringf("%s(%s)", primop.c_str(), a_expr.c_str());
|
||||
|
||||
if ((is_signed && !always_uint))
|
||||
expr = stringf("asUInt(%s)", expr.c_str());
|
||||
|
||||
cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
|
||||
register_reverse_wire_map(y_id, cell->getPort("\\Y"));
|
||||
|
||||
continue;
|
||||
}
|
||||
if (cell->type.in("$add", "$sub", "$mul", "$div", "$mod", "$xor", "$and", "$or", "$eq", "$eqx",
|
||||
"$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl",
|
||||
"$logic_and", "$logic_or"))
|
||||
{
|
||||
string y_id = make_id(cell->name);
|
||||
bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool();
|
||||
int y_width = cell->parameters.at("\\Y_WIDTH").as_int();
|
||||
string a_expr = make_expr(cell->getPort("\\A"));
|
||||
string b_expr = make_expr(cell->getPort("\\B"));
|
||||
wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
|
||||
|
||||
if (cell->parameters.at("\\A_SIGNED").as_bool()) {
|
||||
a_expr = "asSInt(" + a_expr + ")";
|
||||
}
|
||||
// Shift amount is always unsigned, and needn't be padded to result width.
|
||||
if (!cell->type.in("$shr", "$sshr", "$shl", "$sshl")) {
|
||||
if (cell->parameters.at("\\B_SIGNED").as_bool()) {
|
||||
b_expr = "asSInt(" + b_expr + ")";
|
||||
}
|
||||
b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width);
|
||||
}
|
||||
|
||||
a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
|
||||
|
||||
if (cell->parameters.at("\\A_SIGNED").as_bool() & (cell->type == "$shr")) {
|
||||
a_expr = "asUInt(" + a_expr + ")";
|
||||
}
|
||||
|
||||
string primop;
|
||||
bool always_uint = false;
|
||||
if (cell->type == "$add") primop = "add";
|
||||
if (cell->type == "$sub") primop = "sub";
|
||||
if (cell->type == "$mul") primop = "mul";
|
||||
if (cell->type == "$div") primop = "div";
|
||||
if (cell->type == "$mod") primop = "rem";
|
||||
if (cell->type == "$and") {
|
||||
primop = "and";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$or" ) {
|
||||
primop = "or";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$xor") {
|
||||
primop = "xor";
|
||||
always_uint = true;
|
||||
}
|
||||
if ((cell->type == "$eq") | (cell->type == "$eqx")) {
|
||||
primop = "eq";
|
||||
always_uint = true;
|
||||
}
|
||||
if ((cell->type == "$ne") | (cell->type == "$nex")) {
|
||||
primop = "neq";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$gt") {
|
||||
primop = "gt";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$ge") {
|
||||
primop = "geq";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$lt") {
|
||||
primop = "lt";
|
||||
always_uint = true;
|
||||
}
|
||||
if (cell->type == "$le") {
|
||||
primop = "leq";
|
||||
always_uint = true;
|
||||
}
|
||||
if ((cell->type == "$shl") | (cell->type == "$sshl")) primop = "dshl";
|
||||
if ((cell->type == "$shr") | (cell->type == "$sshr")) primop = "dshr";
|
||||
if ((cell->type == "$logic_and")) {
|
||||
primop = "and";
|
||||
a_expr = "neq(" + a_expr + ", UInt(0))";
|
||||
b_expr = "neq(" + b_expr + ", UInt(0))";
|
||||
always_uint = true;
|
||||
}
|
||||
if ((cell->type == "$logic_or")) {
|
||||
primop = "or";
|
||||
a_expr = "neq(" + a_expr + ", UInt(0))";
|
||||
b_expr = "neq(" + b_expr + ", UInt(0))";
|
||||
always_uint = true;
|
||||
}
|
||||
|
||||
if (!cell->parameters.at("\\B_SIGNED").as_bool()) {
|
||||
b_expr = "asUInt(" + b_expr + ")";
|
||||
}
|
||||
|
||||
string expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str());
|
||||
|
||||
if ((is_signed && !always_uint) || cell->type.in("$sub"))
|
||||
expr = stringf("asUInt(%s)", expr.c_str());
|
||||
|
||||
cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
|
||||
register_reverse_wire_map(y_id, cell->getPort("\\Y"));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$mux"))
|
||||
{
|
||||
string y_id = make_id(cell->name);
|
||||
int width = cell->parameters.at("\\WIDTH").as_int();
|
||||
string a_expr = make_expr(cell->getPort("\\A"));
|
||||
string b_expr = make_expr(cell->getPort("\\B"));
|
||||
string s_expr = make_expr(cell->getPort("\\S"));
|
||||
wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), width));
|
||||
|
||||
string expr = stringf("mux(%s, %s, %s)", s_expr.c_str(), b_expr.c_str(), a_expr.c_str());
|
||||
|
||||
cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
|
||||
register_reverse_wire_map(y_id, cell->getPort("\\Y"));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$mem"))
|
||||
{
|
||||
string mem_id = make_id(cell->name);
|
||||
int abits = cell->parameters.at("\\ABITS").as_int();
|
||||
int width = cell->parameters.at("\\WIDTH").as_int();
|
||||
int size = cell->parameters.at("\\SIZE").as_int();
|
||||
int rd_ports = cell->parameters.at("\\RD_PORTS").as_int();
|
||||
int wr_ports = cell->parameters.at("\\WR_PORTS").as_int();
|
||||
|
||||
Const initdata = cell->parameters.at("\\INIT");
|
||||
for (State bit : initdata.bits)
|
||||
if (bit != State::Sx)
|
||||
log_error("Memory with initialization data: %s.%s\n", log_id(module), log_id(cell));
|
||||
|
||||
Const rd_clk_enable = cell->parameters.at("\\RD_CLK_ENABLE");
|
||||
Const wr_clk_enable = cell->parameters.at("\\WR_CLK_ENABLE");
|
||||
Const wr_clk_polarity = cell->parameters.at("\\WR_CLK_POLARITY");
|
||||
|
||||
int offset = cell->parameters.at("\\OFFSET").as_int();
|
||||
if (offset != 0)
|
||||
log_error("Memory with nonzero offset: %s.%s\n", log_id(module), log_id(cell));
|
||||
|
||||
cell_exprs.push_back(stringf(" mem %s:\n", mem_id.c_str()));
|
||||
cell_exprs.push_back(stringf(" data-type => UInt<%d>\n", width));
|
||||
cell_exprs.push_back(stringf(" depth => %d\n", size));
|
||||
|
||||
for (int i = 0; i < rd_ports; i++)
|
||||
cell_exprs.push_back(stringf(" reader => r%d\n", i));
|
||||
|
||||
for (int i = 0; i < wr_ports; i++)
|
||||
cell_exprs.push_back(stringf(" writer => w%d\n", i));
|
||||
|
||||
cell_exprs.push_back(stringf(" read-latency => 0\n"));
|
||||
cell_exprs.push_back(stringf(" write-latency => 1\n"));
|
||||
cell_exprs.push_back(stringf(" read-under-write => undefined\n"));
|
||||
|
||||
for (int i = 0; i < rd_ports; i++)
|
||||
{
|
||||
if (rd_clk_enable[i] != State::S0)
|
||||
log_error("Clocked read port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
|
||||
|
||||
SigSpec data_sig = cell->getPort("\\RD_DATA").extract(i*width, width);
|
||||
string addr_expr = make_expr(cell->getPort("\\RD_ADDR").extract(i*abits, abits));
|
||||
|
||||
cell_exprs.push_back(stringf(" %s.r%d.addr <= %s\n", mem_id.c_str(), i, addr_expr.c_str()));
|
||||
cell_exprs.push_back(stringf(" %s.r%d.en <= UInt<1>(1)\n", mem_id.c_str(), i));
|
||||
cell_exprs.push_back(stringf(" %s.r%d.clk <= asClock(UInt<1>(0))\n", mem_id.c_str(), i));
|
||||
|
||||
register_reverse_wire_map(stringf("%s.r%d.data", mem_id.c_str(), i), data_sig);
|
||||
}
|
||||
|
||||
for (int i = 0; i < wr_ports; i++)
|
||||
{
|
||||
if (wr_clk_enable[i] != State::S1)
|
||||
log_error("Unclocked write port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
|
||||
|
||||
if (wr_clk_polarity[i] != State::S1)
|
||||
log_error("Negedge write port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
|
||||
|
||||
string addr_expr = make_expr(cell->getPort("\\WR_ADDR").extract(i*abits, abits));
|
||||
string data_expr = make_expr(cell->getPort("\\WR_DATA").extract(i*width, width));
|
||||
string clk_expr = make_expr(cell->getPort("\\WR_CLK").extract(i));
|
||||
|
||||
SigSpec wen_sig = cell->getPort("\\WR_EN").extract(i*width, width);
|
||||
string wen_expr = make_expr(wen_sig[0]);
|
||||
|
||||
for (int i = 1; i < GetSize(wen_sig); i++)
|
||||
if (wen_sig[0] != wen_sig[i])
|
||||
log_error("Complex write enable on port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
|
||||
|
||||
cell_exprs.push_back(stringf(" %s.w%d.addr <= %s\n", mem_id.c_str(), i, addr_expr.c_str()));
|
||||
cell_exprs.push_back(stringf(" %s.w%d.data <= %s\n", mem_id.c_str(), i, data_expr.c_str()));
|
||||
cell_exprs.push_back(stringf(" %s.w%d.en <= %s\n", mem_id.c_str(), i, wen_expr.c_str()));
|
||||
cell_exprs.push_back(stringf(" %s.w%d.mask <= UInt<1>(1)\n", mem_id.c_str(), i));
|
||||
cell_exprs.push_back(stringf(" %s.w%d.clk <= asClock(%s)\n", mem_id.c_str(), i, clk_expr.c_str()));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$dff"))
|
||||
{
|
||||
bool clkpol = cell->parameters.at("\\CLK_POLARITY").as_bool();
|
||||
if (clkpol == false)
|
||||
log_error("Negative edge clock on FF %s.%s.\n", log_id(module), log_id(cell));
|
||||
|
||||
string q_id = make_id(cell->name);
|
||||
int width = cell->parameters.at("\\WIDTH").as_int();
|
||||
string expr = make_expr(cell->getPort("\\D"));
|
||||
string clk_expr = "asClock(" + make_expr(cell->getPort("\\CLK")) + ")";
|
||||
|
||||
wire_decls.push_back(stringf(" reg %s: UInt<%d>, %s\n", q_id.c_str(), width, clk_expr.c_str()));
|
||||
|
||||
cell_exprs.push_back(stringf(" %s <= %s\n", q_id.c_str(), expr.c_str()));
|
||||
register_reverse_wire_map(q_id, cell->getPort("\\Q"));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
|
||||
}
|
||||
|
||||
for (auto conn : module->connections())
|
||||
{
|
||||
string y_id = next_id();
|
||||
int y_width = GetSize(conn.first);
|
||||
string expr = make_expr(conn.second);
|
||||
|
||||
wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
|
||||
cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
|
||||
register_reverse_wire_map(y_id, conn.first);
|
||||
}
|
||||
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
string expr;
|
||||
|
||||
if (wire->port_input)
|
||||
continue;
|
||||
|
||||
int cursor = 0;
|
||||
bool is_valid = false;
|
||||
bool make_unconn_id = false;
|
||||
|
||||
while (cursor < wire->width)
|
||||
{
|
||||
int chunk_width = 1;
|
||||
string new_expr;
|
||||
|
||||
SigBit start_bit(wire, cursor);
|
||||
|
||||
if (reverse_wire_map.count(start_bit))
|
||||
{
|
||||
pair<string, int> start_map = reverse_wire_map.at(start_bit);
|
||||
|
||||
while (cursor+chunk_width < wire->width)
|
||||
{
|
||||
SigBit stop_bit(wire, cursor+chunk_width);
|
||||
|
||||
if (reverse_wire_map.count(stop_bit) == 0)
|
||||
break;
|
||||
|
||||
pair<string, int> stop_map = reverse_wire_map.at(stop_bit);
|
||||
stop_map.second -= chunk_width;
|
||||
|
||||
if (start_map != stop_map)
|
||||
break;
|
||||
|
||||
chunk_width++;
|
||||
}
|
||||
|
||||
new_expr = stringf("bits(%s, %d, %d)", start_map.first.c_str(),
|
||||
start_map.second + chunk_width - 1, start_map.second);
|
||||
is_valid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unconn_id.empty()) {
|
||||
unconn_id = next_id();
|
||||
make_unconn_id = true;
|
||||
}
|
||||
new_expr = unconn_id;
|
||||
}
|
||||
|
||||
if (expr.empty())
|
||||
expr = new_expr;
|
||||
else
|
||||
expr = "cat(" + new_expr + ", " + expr + ")";
|
||||
|
||||
cursor += chunk_width;
|
||||
}
|
||||
|
||||
if (is_valid) {
|
||||
if (make_unconn_id) {
|
||||
wire_decls.push_back(stringf(" wire %s: UInt<1>\n", unconn_id.c_str()));
|
||||
wire_decls.push_back(stringf(" %s is invalid\n", unconn_id.c_str()));
|
||||
}
|
||||
wire_exprs.push_back(stringf(" %s <= %s\n", make_id(wire->name), expr.c_str()));
|
||||
} else {
|
||||
if (make_unconn_id) {
|
||||
unconn_id.clear();
|
||||
}
|
||||
wire_decls.push_back(stringf(" %s is invalid\n", make_id(wire->name)));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto str : port_decls)
|
||||
f << str;
|
||||
|
||||
f << stringf("\n");
|
||||
|
||||
for (auto str : wire_decls)
|
||||
f << str;
|
||||
|
||||
f << stringf("\n");
|
||||
|
||||
for (auto str : cell_exprs)
|
||||
f << str;
|
||||
|
||||
f << stringf("\n");
|
||||
|
||||
for (auto str : wire_exprs)
|
||||
f << str;
|
||||
}
|
||||
};
|
||||
|
||||
struct FirrtlBackend : public Backend {
|
||||
FirrtlBackend() : Backend("firrtl", "write design to a FIRRTL file") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" write_firrtl [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Write a FIRRTL netlist of the current design.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-aig") {
|
||||
// aig_mode = true;
|
||||
// continue;
|
||||
// }
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
log_header(design, "Executing FIRRTL backend.\n");
|
||||
|
||||
Module *top = design->top_module();
|
||||
|
||||
if (top == nullptr)
|
||||
log_error("No top module found!\n");
|
||||
|
||||
namecache.clear();
|
||||
autoid_counter = 0;
|
||||
|
||||
for (auto module : design->modules()) {
|
||||
make_id(module->name);
|
||||
for (auto wire : module->wires())
|
||||
if (wire->port_id)
|
||||
make_id(wire->name);
|
||||
}
|
||||
|
||||
*f << stringf("circuit %s:\n", make_id(top->name));
|
||||
|
||||
for (auto module : design->modules())
|
||||
{
|
||||
FirrtlWorker worker(module, *f, design);
|
||||
worker.run();
|
||||
}
|
||||
|
||||
namecache.clear();
|
||||
autoid_counter = 0;
|
||||
}
|
||||
} FirrtlBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,25 @@
|
|||
#!/bin/bash
|
||||
set -ex
|
||||
|
||||
cd ../../
|
||||
make
|
||||
cd backends/firrtl
|
||||
|
||||
../../yosys -q -p 'prep -nordff; write_firrtl test.fir' $1
|
||||
|
||||
firrtl -i test.fir -o test_out.v -ll Info
|
||||
|
||||
../../yosys -p "
|
||||
read_verilog $1
|
||||
rename Top gold
|
||||
|
||||
read_verilog test_out.v
|
||||
rename Top gate
|
||||
|
||||
prep
|
||||
memory_map
|
||||
miter -equiv -flatten gold gate miter
|
||||
hierarchy -top miter
|
||||
|
||||
sat -verify -prove trigger 0 -set-init-zero -seq 10 miter
|
||||
"
|
|
@ -0,0 +1,63 @@
|
|||
module test(
|
||||
input clk, wen,
|
||||
input [7:0] uns,
|
||||
input signed [7:0] a, b,
|
||||
input signed [23:0] c,
|
||||
input signed [2:0] sel,
|
||||
output [15:0] s, d, y, z, u, q, p, mul, div, mod, mux, And, Or, Xor, eq, neq, gt, lt, geq, leq, eqx, shr, sshr, shl, sshl, Land, Lor, Lnot, Not, Neg, pos, Andr, Orr, Xorr, Xnorr, Reduce_bool,
|
||||
output [7:0] PMux
|
||||
);
|
||||
//initial begin
|
||||
//$display("shr = %b", shr);
|
||||
//end
|
||||
assign s = a+{b[6:2], 2'b1};
|
||||
assign d = a-b;
|
||||
assign y = x;
|
||||
assign z[7:0] = s+d;
|
||||
assign z[15:8] = s-d;
|
||||
assign p = a & b | x;
|
||||
assign mul = a * b;
|
||||
assign div = a / b;
|
||||
assign mod = a % b;
|
||||
assign mux = x[0] ? a : b;
|
||||
assign And = a & b;
|
||||
assign Or = a | b;
|
||||
assign Xor = a ^ b;
|
||||
assign Not = ~a;
|
||||
assign Neg = -a;
|
||||
assign eq = a == b;
|
||||
assign neq = a != b;
|
||||
assign gt = a > b;
|
||||
assign lt = a < b;
|
||||
assign geq = a >= b;
|
||||
assign leq = a <= b;
|
||||
assign eqx = a === b;
|
||||
assign shr = a >> b; //0111111111000000
|
||||
assign sshr = a >>> b;
|
||||
assign shl = a << b;
|
||||
assign sshl = a <<< b;
|
||||
assign Land = a && b;
|
||||
assign Lor = a || b;
|
||||
assign Lnot = !a;
|
||||
assign pos = $signed(uns);
|
||||
assign Andr = &a;
|
||||
assign Orr = |a;
|
||||
assign Xorr = ^a;
|
||||
assign Xnorr = ~^a;
|
||||
always @*
|
||||
if(!a) begin
|
||||
Reduce_bool = a;
|
||||
end else begin
|
||||
Reduce_bool = b;
|
||||
end
|
||||
//always @(sel or c or a)
|
||||
// begin
|
||||
// case (sel)
|
||||
// 3'b000: PMux = a;
|
||||
// 3'b001: PMux = c[7:0];
|
||||
// 3'b010: PMux = c[15:8];
|
||||
// 3'b100: PMux = c[23:16];
|
||||
// endcase
|
||||
// end
|
||||
|
||||
endmodule
|
|
@ -219,7 +219,7 @@ void ILANG_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT
|
|||
{
|
||||
f << stringf("%s" "sync ", indent.c_str());
|
||||
switch (sy->type) {
|
||||
if (0) case RTLIL::ST0: f << stringf("low ");
|
||||
case RTLIL::ST0: f << stringf("low ");
|
||||
if (0) case RTLIL::ST1: f << stringf("high ");
|
||||
if (0) case RTLIL::STp: f << stringf("posedge ");
|
||||
if (0) case RTLIL::STn: f << stringf("negedge ");
|
||||
|
@ -382,7 +382,7 @@ PRIVATE_NAMESPACE_BEGIN
|
|||
|
||||
struct IlangBackend : public Backend {
|
||||
IlangBackend() : Backend("ilang", "write design to ilang file") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -395,7 +395,7 @@ struct IlangBackend : public Backend {
|
|||
log(" only write selected parts of the design.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool selected = false;
|
||||
|
||||
|
@ -422,7 +422,7 @@ struct IlangBackend : public Backend {
|
|||
|
||||
struct DumpPass : public Pass {
|
||||
DumpPass() : Pass("dump", "print parts of the design in ilang format") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -445,7 +445,7 @@ struct DumpPass : public Pass {
|
|||
log(" like -outfile but append instead of overwrite\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::string filename;
|
||||
bool flag_m = false, flag_n = false, append = false;
|
||||
|
|
|
@ -46,7 +46,7 @@ static std::string netname(std::set<std::string> &conntypes_code, std::set<std::
|
|||
|
||||
struct IntersynthBackend : public Backend {
|
||||
IntersynthBackend() : Backend("intersynth", "write design to InterSynth netlist file") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -71,7 +71,7 @@ struct IntersynthBackend : public Backend {
|
|||
log("http://www.clifford.at/intersynth/\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing INTERSYNTH backend.\n");
|
||||
log_push();
|
||||
|
|
|
@ -93,8 +93,10 @@ struct JsonWriter
|
|||
f << get_string(param.second.decode_string());
|
||||
else if (GetSize(param.second.bits) > 32)
|
||||
f << get_string(param.second.as_string());
|
||||
else
|
||||
else if ((param.second.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) != 0)
|
||||
f << stringf("%d", param.second.as_int());
|
||||
else
|
||||
f << stringf("%u", param.second.as_int());
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
@ -201,6 +203,8 @@ struct JsonWriter
|
|||
void write_design(Design *design_)
|
||||
{
|
||||
design = design_;
|
||||
design->sort();
|
||||
|
||||
f << stringf("{\n");
|
||||
f << stringf(" \"creator\": %s,\n", get_string(yosys_version_str).c_str());
|
||||
f << stringf(" \"modules\": {\n");
|
||||
|
@ -248,7 +252,7 @@ struct JsonWriter
|
|||
|
||||
struct JsonBackend : public Backend {
|
||||
JsonBackend() : Backend("json", "write design to a JSON file") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -333,6 +337,10 @@ struct JsonBackend : public Backend {
|
|||
log("connected to a constant driver are denoted as string \"0\" or \"1\" instead of\n");
|
||||
log("a number.\n");
|
||||
log("\n");
|
||||
log("Numeric parameter and attribute values up to 32 bits are written as decimal\n");
|
||||
log("values. Numbers larger than that are written as string holding the binary\n");
|
||||
log("representation of the value.\n");
|
||||
log("\n");
|
||||
log("For example the following Verilog code:\n");
|
||||
log("\n");
|
||||
log(" module test(input x, y);\n");
|
||||
|
@ -452,7 +460,7 @@ struct JsonBackend : public Backend {
|
|||
log("format. A program processing this format must ignore all unknown fields.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool aig_mode = false;
|
||||
|
||||
|
@ -476,7 +484,7 @@ struct JsonBackend : public Backend {
|
|||
|
||||
struct JsonPass : public Pass {
|
||||
JsonPass() : Pass("json", "write design in JSON format") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -493,7 +501,7 @@ struct JsonPass : public Pass {
|
|||
log("See 'help write_json' for a description of the JSON format used.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::string filename;
|
||||
bool aig_mode = false;
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
yosys.pb.cc
|
||||
yosys.pb.h
|
|
@ -0,0 +1,8 @@
|
|||
ifeq ($(ENABLE_PROTOBUF),1)
|
||||
|
||||
backends/protobuf/yosys.pb.cc backends/protobuf/yosys.pb.h: misc/yosys.proto
|
||||
$(Q) cd misc && protoc --cpp_out "../backends/protobuf" yosys.proto
|
||||
|
||||
OBJS += backends/protobuf/protobuf.o backends/protobuf/yosys.pb.o
|
||||
|
||||
endif
|
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
|
||||
*
|
||||
* 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 <google/protobuf/text_format.h>
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/cellaigs.h"
|
||||
#include "kernel/log.h"
|
||||
#include "yosys.pb.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct ProtobufDesignSerializer
|
||||
{
|
||||
bool aig_mode_;
|
||||
bool use_selection_;
|
||||
yosys::pb::Design *pb_;
|
||||
|
||||
Design *design_;
|
||||
Module *module_;
|
||||
|
||||
SigMap sigmap_;
|
||||
int sigidcounter_;
|
||||
dict<SigBit, uint64_t> sigids_;
|
||||
pool<Aig> aig_models_;
|
||||
|
||||
|
||||
ProtobufDesignSerializer(bool use_selection, bool aig_mode) :
|
||||
aig_mode_(aig_mode), use_selection_(use_selection) { }
|
||||
|
||||
string get_name(IdString name)
|
||||
{
|
||||
return RTLIL::unescape_id(name);
|
||||
}
|
||||
|
||||
|
||||
void serialize_parameters(google::protobuf::Map<std::string, yosys::pb::Parameter> *out,
|
||||
const dict<IdString, Const> ¶meters)
|
||||
{
|
||||
for (auto ¶m : parameters) {
|
||||
std::string key = get_name(param.first);
|
||||
|
||||
|
||||
yosys::pb::Parameter pb_param;
|
||||
|
||||
if ((param.second.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) != 0) {
|
||||
pb_param.set_str(param.second.decode_string());
|
||||
} else if (GetSize(param.second.bits) > 64) {
|
||||
pb_param.set_str(param.second.as_string());
|
||||
} else {
|
||||
pb_param.set_int_(param.second.as_int());
|
||||
}
|
||||
|
||||
(*out)[key] = pb_param;
|
||||
}
|
||||
}
|
||||
|
||||
void get_bits(yosys::pb::BitVector *out, SigSpec sig)
|
||||
{
|
||||
for (auto bit : sigmap_(sig)) {
|
||||
auto sig = out->add_signal();
|
||||
|
||||
// Constant driver.
|
||||
if (bit.wire == nullptr) {
|
||||
if (bit == State::S0) sig->set_constant(sig->CONSTANT_DRIVER_LOW);
|
||||
else if (bit == State::S1) sig->set_constant(sig->CONSTANT_DRIVER_HIGH);
|
||||
else if (bit == State::Sz) sig->set_constant(sig->CONSTANT_DRIVER_Z);
|
||||
else sig->set_constant(sig->CONSTANT_DRIVER_X);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Signal - give it a unique identifier.
|
||||
if (sigids_.count(bit) == 0) {
|
||||
sigids_[bit] = sigidcounter_++;
|
||||
}
|
||||
sig->set_id(sigids_[bit]);
|
||||
}
|
||||
}
|
||||
|
||||
void serialize_module(yosys::pb::Module* out, Module *module)
|
||||
{
|
||||
module_ = module;
|
||||
log_assert(module_->design == design_);
|
||||
sigmap_.set(module_);
|
||||
sigids_.clear();
|
||||
sigidcounter_ = 0;
|
||||
|
||||
serialize_parameters(out->mutable_attribute(), module_->attributes);
|
||||
|
||||
for (auto n : module_->ports) {
|
||||
Wire *w = module->wire(n);
|
||||
if (use_selection_ && !module_->selected(w))
|
||||
continue;
|
||||
|
||||
yosys::pb::Module::Port pb_port;
|
||||
pb_port.set_direction(w->port_input ? w->port_output ?
|
||||
yosys::pb::DIRECTION_INOUT : yosys::pb::DIRECTION_INPUT : yosys::pb::DIRECTION_OUTPUT);
|
||||
get_bits(pb_port.mutable_bits(), w);
|
||||
(*out->mutable_port())[get_name(n)] = pb_port;
|
||||
}
|
||||
|
||||
for (auto c : module_->cells()) {
|
||||
if (use_selection_ && !module_->selected(c))
|
||||
continue;
|
||||
|
||||
yosys::pb::Module::Cell pb_cell;
|
||||
pb_cell.set_hide_name(c->name[0] == '$');
|
||||
pb_cell.set_type(get_name(c->type));
|
||||
|
||||
if (aig_mode_) {
|
||||
Aig aig(c);
|
||||
if (aig.name.empty())
|
||||
continue;
|
||||
pb_cell.set_model(aig.name);
|
||||
aig_models_.insert(aig);
|
||||
}
|
||||
serialize_parameters(pb_cell.mutable_parameter(), c->parameters);
|
||||
serialize_parameters(pb_cell.mutable_attribute(), c->attributes);
|
||||
|
||||
if (c->known()) {
|
||||
for (auto &conn : c->connections()) {
|
||||
yosys::pb::Direction direction = yosys::pb::DIRECTION_OUTPUT;
|
||||
if (c->input(conn.first))
|
||||
direction = c->output(conn.first) ? yosys::pb::DIRECTION_INOUT : yosys::pb::DIRECTION_INPUT;
|
||||
(*pb_cell.mutable_port_direction())[get_name(conn.first)] = direction;
|
||||
}
|
||||
}
|
||||
for (auto &conn : c->connections()) {
|
||||
yosys::pb::BitVector vec;
|
||||
get_bits(&vec, conn.second);
|
||||
(*pb_cell.mutable_connection())[get_name(conn.first)] = vec;
|
||||
}
|
||||
|
||||
(*out->mutable_cell())[get_name(c->name)] = pb_cell;
|
||||
}
|
||||
|
||||
for (auto w : module_->wires()) {
|
||||
if (use_selection_ && !module_->selected(w))
|
||||
continue;
|
||||
|
||||
auto netname = out->add_netname();
|
||||
netname->set_hide_name(w->name[0] == '$');
|
||||
get_bits(netname->mutable_bits(), w);
|
||||
serialize_parameters(netname->mutable_attributes(), w->attributes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void serialize_models(google::protobuf::Map<string, yosys::pb::Model> *models)
|
||||
{
|
||||
for (auto &aig : aig_models_) {
|
||||
yosys::pb::Model pb_model;
|
||||
for (auto &node : aig.nodes) {
|
||||
auto pb_node = pb_model.add_node();
|
||||
if (node.portbit >= 0) {
|
||||
if (node.inverter) {
|
||||
pb_node->set_type(pb_node->TYPE_NPORT);
|
||||
} else {
|
||||
pb_node->set_type(pb_node->TYPE_PORT);
|
||||
}
|
||||
auto port = pb_node->mutable_port();
|
||||
port->set_portname(log_id(node.portname));
|
||||
port->set_bitindex(node.portbit);
|
||||
} else if (node.left_parent < 0 && node.right_parent < 0) {
|
||||
if (node.inverter) {
|
||||
pb_node->set_type(pb_node->TYPE_TRUE);
|
||||
} else {
|
||||
pb_node->set_type(pb_node->TYPE_FALSE);
|
||||
}
|
||||
} else {
|
||||
if (node.inverter) {
|
||||
pb_node->set_type(pb_node->TYPE_NAND);
|
||||
} else {
|
||||
pb_node->set_type(pb_node->TYPE_AND);
|
||||
}
|
||||
auto gate = pb_node->mutable_gate();
|
||||
gate->set_left(node.left_parent);
|
||||
gate->set_right(node.right_parent);
|
||||
}
|
||||
for (auto &op : node.outports) {
|
||||
auto pb_op = pb_node->add_out_port();
|
||||
pb_op->set_name(log_id(op.first));
|
||||
pb_op->set_bit_index(op.second);
|
||||
}
|
||||
}
|
||||
(*models)[aig.name] = pb_model;
|
||||
}
|
||||
}
|
||||
|
||||
void serialize_design(yosys::pb::Design *pb, Design *design)
|
||||
{
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
pb_ = pb;
|
||||
pb_->Clear();
|
||||
pb_->set_creator(yosys_version_str);
|
||||
|
||||
design_ = design;
|
||||
design_->sort();
|
||||
|
||||
auto modules = use_selection_ ? design_->selected_modules() : design_->modules();
|
||||
for (auto mod : modules) {
|
||||
yosys::pb::Module pb_mod;
|
||||
serialize_module(&pb_mod, mod);
|
||||
(*pb->mutable_modules())[mod->name.str()] = pb_mod;
|
||||
}
|
||||
|
||||
serialize_models(pb_->mutable_models());
|
||||
}
|
||||
};
|
||||
|
||||
struct ProtobufBackend : public Backend {
|
||||
ProtobufBackend(): Backend("protobuf", "write design to a Protocol Buffer file") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" write_protobuf [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Write a JSON netlist of the current design.\n");
|
||||
log("\n");
|
||||
log(" -aig\n");
|
||||
log(" include AIG models for the different gate types\n");
|
||||
log("\n");
|
||||
log(" -text\n");
|
||||
log(" output protobuf in Text/ASCII representation\n");
|
||||
log("\n");
|
||||
log("The schema of the output Protocol Buffer is defined in misc/yosys.pb in the\n");
|
||||
log("Yosys source code distribution.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool aig_mode = false;
|
||||
bool text_mode = false;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-aig") {
|
||||
aig_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-text") {
|
||||
text_mode = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
log_header(design, "Executing Protobuf backend.\n");
|
||||
|
||||
yosys::pb::Design pb;
|
||||
ProtobufDesignSerializer serializer(false, aig_mode);
|
||||
serializer.serialize_design(&pb, design);
|
||||
|
||||
if (text_mode) {
|
||||
string out;
|
||||
google::protobuf::TextFormat::PrintToString(pb, &out);
|
||||
*f << out;
|
||||
} else {
|
||||
pb.SerializeToOstream(f);
|
||||
}
|
||||
}
|
||||
} ProtobufBackend;
|
||||
|
||||
struct ProtobufPass : public Pass {
|
||||
ProtobufPass() : Pass("protobuf", "write design in Protobuf format") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" protobuf [options] [selection]\n");
|
||||
log("\n");
|
||||
log("Write a JSON netlist of all selected objects.\n");
|
||||
log("\n");
|
||||
log(" -o <filename>\n");
|
||||
log(" write to the specified file.\n");
|
||||
log("\n");
|
||||
log(" -aig\n");
|
||||
log(" include AIG models for the different gate types\n");
|
||||
log("\n");
|
||||
log(" -text\n");
|
||||
log(" output protobuf in Text/ASCII representation\n");
|
||||
log("\n");
|
||||
log("The schema of the output Protocol Buffer is defined in misc/yosys.pb in the\n");
|
||||
log("Yosys source code distribution.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::string filename;
|
||||
bool aig_mode = false;
|
||||
bool text_mode = false;
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-o" && argidx+1 < args.size()) {
|
||||
filename = args[++argidx];
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-aig") {
|
||||
aig_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-text") {
|
||||
text_mode = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
std::ostream *f;
|
||||
std::stringstream buf;
|
||||
|
||||
if (!filename.empty()) {
|
||||
std::ofstream *ff = new std::ofstream;
|
||||
ff->open(filename.c_str(), std::ofstream::trunc);
|
||||
if (ff->fail()) {
|
||||
delete ff;
|
||||
log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
|
||||
}
|
||||
f = ff;
|
||||
} else {
|
||||
f = &buf;
|
||||
}
|
||||
|
||||
yosys::pb::Design pb;
|
||||
ProtobufDesignSerializer serializer(true, aig_mode);
|
||||
serializer.serialize_design(&pb, design);
|
||||
|
||||
if (text_mode) {
|
||||
string out;
|
||||
google::protobuf::TextFormat::PrintToString(pb, &out);
|
||||
*f << out;
|
||||
} else {
|
||||
pb.SerializeToOstream(f);
|
||||
}
|
||||
|
||||
if (!filename.empty()) {
|
||||
delete f;
|
||||
} else {
|
||||
log("%s", buf.str().c_str());
|
||||
}
|
||||
}
|
||||
} ProtobufPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END;
|
|
@ -0,0 +1,2 @@
|
|||
test00_tb
|
||||
test00_uut.c
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/simplec/simplec.o
|
||||
|
|
@ -0,0 +1,810 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/utils.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct HierDirtyFlags;
|
||||
|
||||
static pool<string> reserved_cids;
|
||||
static dict<IdString, string> id2cid;
|
||||
|
||||
static string cid(IdString id)
|
||||
{
|
||||
if (id2cid.count(id) == 0)
|
||||
{
|
||||
string s = id.str();
|
||||
if (GetSize(s) < 2) log_abort();
|
||||
|
||||
if (s[0] == '\\')
|
||||
s = s.substr(1);
|
||||
|
||||
if ('0' <= s[0] && s[0] <= '9') {
|
||||
s = "_" + s;
|
||||
}
|
||||
|
||||
for (int i = 0; i < GetSize(s); i++) {
|
||||
if ('0' <= s[i] && s[i] <= '9') continue;
|
||||
if ('A' <= s[i] && s[i] <= 'Z') continue;
|
||||
if ('a' <= s[i] && s[i] <= 'z') continue;
|
||||
s[i] = '_';
|
||||
}
|
||||
|
||||
while (reserved_cids.count(s))
|
||||
s += "_";
|
||||
|
||||
reserved_cids.insert(s);
|
||||
id2cid[id] = s;
|
||||
}
|
||||
|
||||
return id2cid.at(id);
|
||||
}
|
||||
|
||||
struct HierDirtyFlags
|
||||
{
|
||||
int dirty;
|
||||
Module *module;
|
||||
IdString hiername;
|
||||
HierDirtyFlags *parent;
|
||||
pool<SigBit> dirty_bits;
|
||||
pool<Cell*> dirty_cells;
|
||||
pool<SigBit> sticky_dirty_bits;
|
||||
dict<IdString, HierDirtyFlags*> children;
|
||||
string prefix, log_prefix;
|
||||
|
||||
HierDirtyFlags(Module *module, IdString hiername, HierDirtyFlags *parent, const string &prefix, const string &log_prefix) :
|
||||
dirty(0), module(module), hiername(hiername), parent(parent), prefix(prefix), log_prefix(log_prefix)
|
||||
{
|
||||
for (Cell *cell : module->cells()) {
|
||||
Module *mod = module->design->module(cell->type);
|
||||
if (mod) children[cell->name] = new HierDirtyFlags(mod, cell->name, this,
|
||||
prefix + cid(cell->name) + ".", log_prefix + "." + prefix + log_id(cell->name));
|
||||
}
|
||||
}
|
||||
|
||||
~HierDirtyFlags()
|
||||
{
|
||||
for (auto &child : children)
|
||||
delete child.second;
|
||||
}
|
||||
|
||||
void set_dirty(SigBit bit)
|
||||
{
|
||||
if (dirty_bits.count(bit))
|
||||
return;
|
||||
|
||||
dirty_bits.insert(bit);
|
||||
sticky_dirty_bits.insert(bit);
|
||||
|
||||
HierDirtyFlags *p = this;
|
||||
while (p != nullptr) {
|
||||
p->dirty++;
|
||||
p = p->parent;
|
||||
}
|
||||
}
|
||||
|
||||
void unset_dirty(SigBit bit)
|
||||
{
|
||||
if (dirty_bits.count(bit) == 0)
|
||||
return;
|
||||
|
||||
dirty_bits.erase(bit);
|
||||
|
||||
HierDirtyFlags *p = this;
|
||||
while (p != nullptr) {
|
||||
p->dirty--;
|
||||
log_assert(p->dirty >= 0);
|
||||
p = p->parent;
|
||||
}
|
||||
}
|
||||
|
||||
void set_dirty(Cell *cell)
|
||||
{
|
||||
if (dirty_cells.count(cell))
|
||||
return;
|
||||
|
||||
dirty_cells.insert(cell);
|
||||
|
||||
HierDirtyFlags *p = this;
|
||||
while (p != nullptr) {
|
||||
p->dirty++;
|
||||
p = p->parent;
|
||||
}
|
||||
}
|
||||
|
||||
void unset_dirty(Cell *cell)
|
||||
{
|
||||
if (dirty_cells.count(cell) == 0)
|
||||
return;
|
||||
|
||||
dirty_cells.erase(cell);
|
||||
|
||||
HierDirtyFlags *p = this;
|
||||
while (p != nullptr) {
|
||||
p->dirty--;
|
||||
log_assert(p->dirty >= 0);
|
||||
p = p->parent;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct SimplecWorker
|
||||
{
|
||||
bool verbose = false;
|
||||
int max_uintsize = 32;
|
||||
|
||||
Design *design;
|
||||
dict<Module*, SigMap> sigmaps;
|
||||
|
||||
vector<string> signal_declarations;
|
||||
pool<int> generated_sigtypes;
|
||||
|
||||
vector<string> util_declarations;
|
||||
pool<string> generated_utils;
|
||||
|
||||
vector<string> struct_declarations;
|
||||
pool<IdString> generated_structs;
|
||||
|
||||
vector<string> funct_declarations;
|
||||
|
||||
dict<Module*, dict<SigBit, pool<tuple<Cell*, IdString, int>>>> bit2cell;
|
||||
dict<Module*, dict<SigBit, pool<SigBit>>> bit2output;
|
||||
dict<Module*, pool<SigBit>> driven_bits;
|
||||
|
||||
dict<Cell*, int> topoidx;
|
||||
|
||||
pool<string> activated_cells;
|
||||
pool<string> reactivated_cells;
|
||||
|
||||
SimplecWorker(Design *design) : design(design)
|
||||
{
|
||||
}
|
||||
|
||||
string sigtype(int n)
|
||||
{
|
||||
string struct_name = stringf("signal%d_t", n);
|
||||
|
||||
if (generated_sigtypes.count(n) == 0)
|
||||
{
|
||||
signal_declarations.push_back("");
|
||||
signal_declarations.push_back(stringf("#ifndef YOSYS_SIMPLEC_SIGNAL%d_T", n));
|
||||
signal_declarations.push_back(stringf("#define YOSYS_SIMPLEC_SIGNAL%d_T", n));
|
||||
signal_declarations.push_back(stringf("typedef struct {"));
|
||||
|
||||
for (int k = 8; k <= max_uintsize; k = 2*k)
|
||||
if (n <= k && k <= max_uintsize) {
|
||||
signal_declarations.push_back(stringf(" uint%d_t value_%d_0 : %d;", k, n-1, n));
|
||||
goto end_struct;
|
||||
}
|
||||
|
||||
for (int k = 0; k < n; k += max_uintsize) {
|
||||
int bits = std::min(max_uintsize, n-k);
|
||||
signal_declarations.push_back(stringf(" uint%d_t value_%d_%d : %d;", max_uintsize, k+bits-1, k, bits));
|
||||
}
|
||||
|
||||
end_struct:
|
||||
signal_declarations.push_back(stringf("} signal%d_t;", n));
|
||||
signal_declarations.push_back(stringf("#endif"));
|
||||
generated_sigtypes.insert(n);
|
||||
}
|
||||
|
||||
return struct_name;
|
||||
}
|
||||
|
||||
void util_ifdef_guard(string s)
|
||||
{
|
||||
for (int i = 0; i < GetSize(s); i++)
|
||||
if ('a' <= s[i] && s[i] <= 'z')
|
||||
s[i] -= 'a' - 'A';
|
||||
|
||||
util_declarations.push_back("");
|
||||
util_declarations.push_back(stringf("#ifndef %s", s.c_str()));
|
||||
util_declarations.push_back(stringf("#define %s", s.c_str()));
|
||||
}
|
||||
|
||||
string util_get_bit(const string &signame, int n, int idx)
|
||||
{
|
||||
if (n == 1 && idx == 0)
|
||||
return signame + ".value_0_0";
|
||||
|
||||
string util_name = stringf("yosys_simplec_get_bit_%d_of_%d", idx, n);
|
||||
|
||||
if (generated_utils.count(util_name) == 0)
|
||||
{
|
||||
util_ifdef_guard(util_name);
|
||||
util_declarations.push_back(stringf("static inline bool %s(const %s *sig)", util_name.c_str(), sigtype(n).c_str()));
|
||||
util_declarations.push_back(stringf("{"));
|
||||
|
||||
int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize;
|
||||
string value_name = stringf("value_%d_%d", std::min(n-1, (word_idx+1)*max_uintsize-1), word_idx*max_uintsize);
|
||||
|
||||
util_declarations.push_back(stringf(" return (sig->%s >> %d) & 1;", value_name.c_str(), word_offset));
|
||||
|
||||
util_declarations.push_back(stringf("}"));
|
||||
util_declarations.push_back(stringf("#endif"));
|
||||
generated_utils.insert(util_name);
|
||||
}
|
||||
|
||||
return stringf("%s(&%s)", util_name.c_str(), signame.c_str());
|
||||
}
|
||||
|
||||
string util_set_bit(const string &signame, int n, int idx, const string &expr)
|
||||
{
|
||||
if (n == 1 && idx == 0)
|
||||
return stringf(" %s.value_0_0 = %s;", signame.c_str(), expr.c_str());
|
||||
|
||||
string util_name = stringf("yosys_simplec_set_bit_%d_of_%d", idx, n);
|
||||
|
||||
if (generated_utils.count(util_name) == 0)
|
||||
{
|
||||
util_ifdef_guard(util_name);
|
||||
util_declarations.push_back(stringf("static inline void %s(%s *sig, bool value)", util_name.c_str(), sigtype(n).c_str()));
|
||||
util_declarations.push_back(stringf("{"));
|
||||
|
||||
int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize;
|
||||
string value_name = stringf("value_%d_%d", std::min(n-1, (word_idx+1)*max_uintsize-1), word_idx*max_uintsize);
|
||||
|
||||
#if 0
|
||||
util_declarations.push_back(stringf(" if (value)"));
|
||||
util_declarations.push_back(stringf(" sig->%s |= 1UL << %d;", value_name.c_str(), word_offset));
|
||||
util_declarations.push_back(stringf(" else"));
|
||||
util_declarations.push_back(stringf(" sig->%s &= ~(1UL << %d);", value_name.c_str(), word_offset));
|
||||
#else
|
||||
util_declarations.push_back(stringf(" sig->%s = (sig->%s & ~((uint%d_t)1 << %d)) | ((uint%d_t)value << %d);",
|
||||
value_name.c_str(), value_name.c_str(), max_uintsize, word_offset, max_uintsize, word_offset));
|
||||
#endif
|
||||
|
||||
util_declarations.push_back(stringf("}"));
|
||||
util_declarations.push_back(stringf("#endif"));
|
||||
generated_utils.insert(util_name);
|
||||
}
|
||||
|
||||
return stringf(" %s(&%s, %s);", util_name.c_str(), signame.c_str(), expr.c_str());
|
||||
}
|
||||
|
||||
void create_module_struct(Module *mod)
|
||||
{
|
||||
if (generated_structs.count(mod->name))
|
||||
return;
|
||||
|
||||
generated_structs.insert(mod->name);
|
||||
sigmaps[mod].set(mod);
|
||||
|
||||
for (Wire *w : mod->wires())
|
||||
{
|
||||
if (w->port_output)
|
||||
for (auto bit : SigSpec(w))
|
||||
bit2output[mod][sigmaps.at(mod)(bit)].insert(bit);
|
||||
}
|
||||
|
||||
for (Cell *c : mod->cells())
|
||||
{
|
||||
for (auto &conn : c->connections())
|
||||
{
|
||||
if (!c->input(conn.first)) {
|
||||
for (auto bit : sigmaps.at(mod)(conn.second))
|
||||
driven_bits[mod].insert(bit);
|
||||
continue;
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
for (auto bit : sigmaps.at(mod)(conn.second))
|
||||
bit2cell[mod][bit].insert(tuple<Cell*, IdString, int>(c, conn.first, idx++));
|
||||
}
|
||||
|
||||
if (design->module(c->type))
|
||||
create_module_struct(design->module(c->type));
|
||||
}
|
||||
|
||||
TopoSort<IdString> topo;
|
||||
|
||||
for (Cell *c : mod->cells())
|
||||
{
|
||||
topo.node(c->name);
|
||||
|
||||
for (auto &conn : c->connections())
|
||||
{
|
||||
if (!c->input(conn.first))
|
||||
continue;
|
||||
|
||||
for (auto bit : sigmaps.at(mod)(conn.second))
|
||||
for (auto &it : bit2cell[mod][bit])
|
||||
topo.edge(c->name, std::get<0>(it)->name);
|
||||
}
|
||||
}
|
||||
|
||||
topo.analyze_loops = false;
|
||||
topo.sort();
|
||||
|
||||
for (int i = 0; i < GetSize(topo.sorted); i++)
|
||||
topoidx[mod->cell(topo.sorted[i])] = i;
|
||||
|
||||
string ifdef_name = stringf("yosys_simplec_%s_state_t", cid(mod->name).c_str());
|
||||
|
||||
for (int i = 0; i < GetSize(ifdef_name); i++)
|
||||
if ('a' <= ifdef_name[i] && ifdef_name[i] <= 'z')
|
||||
ifdef_name[i] -= 'a' - 'A';
|
||||
|
||||
struct_declarations.push_back("");
|
||||
struct_declarations.push_back(stringf("#ifndef %s", ifdef_name.c_str()));
|
||||
struct_declarations.push_back(stringf("#define %s", ifdef_name.c_str()));
|
||||
struct_declarations.push_back(stringf("struct %s_state_t", cid(mod->name).c_str()));
|
||||
struct_declarations.push_back("{");
|
||||
|
||||
struct_declarations.push_back(" // Input Ports");
|
||||
for (Wire *w : mod->wires())
|
||||
if (w->port_input)
|
||||
struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w)));
|
||||
|
||||
struct_declarations.push_back("");
|
||||
struct_declarations.push_back(" // Output Ports");
|
||||
for (Wire *w : mod->wires())
|
||||
if (!w->port_input && w->port_output)
|
||||
struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w)));
|
||||
|
||||
struct_declarations.push_back("");
|
||||
struct_declarations.push_back(" // Internal Wires");
|
||||
for (Wire *w : mod->wires())
|
||||
if (!w->port_input && !w->port_output)
|
||||
struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w)));
|
||||
|
||||
for (Cell *c : mod->cells())
|
||||
if (design->module(c->type))
|
||||
struct_declarations.push_back(stringf(" struct %s_state_t %s; // %s", cid(c->type).c_str(), cid(c->name).c_str(), log_id(c)));
|
||||
|
||||
struct_declarations.push_back(stringf("};"));
|
||||
struct_declarations.push_back("#endif");
|
||||
}
|
||||
|
||||
void eval_cell(HierDirtyFlags *work, Cell *cell)
|
||||
{
|
||||
if (cell->type.in("$_BUF_", "$_NOT_"))
|
||||
{
|
||||
SigBit a = sigmaps.at(work->module)(cell->getPort("\\A"));
|
||||
SigBit y = sigmaps.at(work->module)(cell->getPort("\\Y"));
|
||||
|
||||
string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0";
|
||||
string expr;
|
||||
|
||||
if (cell->type == "$_BUF_") expr = a_expr;
|
||||
if (cell->type == "$_NOT_") expr = "!" + a_expr;
|
||||
|
||||
log_assert(y.wire);
|
||||
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
|
||||
stringf(" // %s (%s)", log_id(cell), log_id(cell->type)));
|
||||
|
||||
work->set_dirty(y);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_"))
|
||||
{
|
||||
SigBit a = sigmaps.at(work->module)(cell->getPort("\\A"));
|
||||
SigBit b = sigmaps.at(work->module)(cell->getPort("\\B"));
|
||||
SigBit y = sigmaps.at(work->module)(cell->getPort("\\Y"));
|
||||
|
||||
string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0";
|
||||
string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0";
|
||||
string expr;
|
||||
|
||||
if (cell->type == "$_AND_") expr = stringf("%s & %s", a_expr.c_str(), b_expr.c_str());
|
||||
if (cell->type == "$_NAND_") expr = stringf("!(%s & %s)", a_expr.c_str(), b_expr.c_str());
|
||||
if (cell->type == "$_OR_") expr = stringf("%s | %s", a_expr.c_str(), b_expr.c_str());
|
||||
if (cell->type == "$_NOR_") expr = stringf("!(%s | %s)", a_expr.c_str(), b_expr.c_str());
|
||||
if (cell->type == "$_XOR_") expr = stringf("%s ^ %s", a_expr.c_str(), b_expr.c_str());
|
||||
if (cell->type == "$_XNOR_") expr = stringf("!(%s ^ %s)", a_expr.c_str(), b_expr.c_str());
|
||||
if (cell->type == "$_ANDNOT_") expr = stringf("%s & (!%s)", a_expr.c_str(), b_expr.c_str());
|
||||
if (cell->type == "$_ORNOT_") expr = stringf("%s | (!%s)", a_expr.c_str(), b_expr.c_str());
|
||||
|
||||
log_assert(y.wire);
|
||||
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
|
||||
stringf(" // %s (%s)", log_id(cell), log_id(cell->type)));
|
||||
|
||||
work->set_dirty(y);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell->type.in("$_AOI3_", "$_OAI3_"))
|
||||
{
|
||||
SigBit a = sigmaps.at(work->module)(cell->getPort("\\A"));
|
||||
SigBit b = sigmaps.at(work->module)(cell->getPort("\\B"));
|
||||
SigBit c = sigmaps.at(work->module)(cell->getPort("\\C"));
|
||||
SigBit y = sigmaps.at(work->module)(cell->getPort("\\Y"));
|
||||
|
||||
string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0";
|
||||
string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0";
|
||||
string c_expr = c.wire ? util_get_bit(work->prefix + cid(c.wire->name), c.wire->width, c.offset) : c.data ? "1" : "0";
|
||||
string expr;
|
||||
|
||||
if (cell->type == "$_AOI3_") expr = stringf("!((%s & %s) | %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str());
|
||||
if (cell->type == "$_OAI3_") expr = stringf("!((%s | %s) & %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str());
|
||||
|
||||
log_assert(y.wire);
|
||||
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
|
||||
stringf(" // %s (%s)", log_id(cell), log_id(cell->type)));
|
||||
|
||||
work->set_dirty(y);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell->type.in("$_AOI4_", "$_OAI4_"))
|
||||
{
|
||||
SigBit a = sigmaps.at(work->module)(cell->getPort("\\A"));
|
||||
SigBit b = sigmaps.at(work->module)(cell->getPort("\\B"));
|
||||
SigBit c = sigmaps.at(work->module)(cell->getPort("\\C"));
|
||||
SigBit d = sigmaps.at(work->module)(cell->getPort("\\D"));
|
||||
SigBit y = sigmaps.at(work->module)(cell->getPort("\\Y"));
|
||||
|
||||
string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0";
|
||||
string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0";
|
||||
string c_expr = c.wire ? util_get_bit(work->prefix + cid(c.wire->name), c.wire->width, c.offset) : c.data ? "1" : "0";
|
||||
string d_expr = d.wire ? util_get_bit(work->prefix + cid(d.wire->name), d.wire->width, d.offset) : d.data ? "1" : "0";
|
||||
string expr;
|
||||
|
||||
if (cell->type == "$_AOI4_") expr = stringf("!((%s & %s) | (%s & %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str());
|
||||
if (cell->type == "$_OAI4_") expr = stringf("!((%s | %s) & (%s | %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str());
|
||||
|
||||
log_assert(y.wire);
|
||||
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
|
||||
stringf(" // %s (%s)", log_id(cell), log_id(cell->type)));
|
||||
|
||||
work->set_dirty(y);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell->type == "$_MUX_")
|
||||
{
|
||||
SigBit a = sigmaps.at(work->module)(cell->getPort("\\A"));
|
||||
SigBit b = sigmaps.at(work->module)(cell->getPort("\\B"));
|
||||
SigBit s = sigmaps.at(work->module)(cell->getPort("\\S"));
|
||||
SigBit y = sigmaps.at(work->module)(cell->getPort("\\Y"));
|
||||
|
||||
string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0";
|
||||
string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0";
|
||||
string s_expr = s.wire ? util_get_bit(work->prefix + cid(s.wire->name), s.wire->width, s.offset) : s.data ? "1" : "0";
|
||||
|
||||
// casts to bool are a workaround for CBMC bug (https://github.com/diffblue/cbmc/issues/933)
|
||||
string expr = stringf("%s ? (bool)%s : (bool)%s", s_expr.c_str(), b_expr.c_str(), a_expr.c_str());
|
||||
|
||||
log_assert(y.wire);
|
||||
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
|
||||
stringf(" // %s (%s)", log_id(cell), log_id(cell->type)));
|
||||
|
||||
work->set_dirty(y);
|
||||
return;
|
||||
}
|
||||
|
||||
log_error("No C model for %s available at the moment (FIXME).\n", log_id(cell->type));
|
||||
}
|
||||
|
||||
void eval_dirty(HierDirtyFlags *work)
|
||||
{
|
||||
while (work->dirty)
|
||||
{
|
||||
if (verbose && (!work->dirty_bits.empty() || !work->dirty_cells.empty()))
|
||||
log(" In %s:\n", work->log_prefix.c_str());
|
||||
|
||||
while (!work->dirty_bits.empty() || !work->dirty_cells.empty())
|
||||
{
|
||||
if (!work->dirty_bits.empty())
|
||||
{
|
||||
SigSpec dirtysig(work->dirty_bits);
|
||||
dirtysig.sort_and_unify();
|
||||
|
||||
for (SigChunk chunk : dirtysig.chunks()) {
|
||||
if (chunk.wire == nullptr)
|
||||
continue;
|
||||
if (verbose)
|
||||
log(" Propagating %s.%s[%d:%d].\n", work->log_prefix.c_str(), log_id(chunk.wire), chunk.offset+chunk.width-1, chunk.offset);
|
||||
funct_declarations.push_back(stringf(" // Updated signal in %s: %s", work->log_prefix.c_str(), log_signal(chunk)));
|
||||
}
|
||||
|
||||
for (SigBit bit : dirtysig)
|
||||
{
|
||||
if (bit2output[work->module].count(bit) && work->parent)
|
||||
for (auto outbit : bit2output[work->module][bit])
|
||||
{
|
||||
Module *parent_mod = work->parent->module;
|
||||
Cell *parent_cell = parent_mod->cell(work->hiername);
|
||||
|
||||
IdString port_name = outbit.wire->name;
|
||||
int port_offset = outbit.offset;
|
||||
SigBit parent_bit = sigmaps.at(parent_mod)(parent_cell->getPort(port_name)[port_offset]);
|
||||
|
||||
log_assert(bit.wire && parent_bit.wire);
|
||||
funct_declarations.push_back(util_set_bit(work->parent->prefix + cid(parent_bit.wire->name), parent_bit.wire->width, parent_bit.offset,
|
||||
util_get_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset)));
|
||||
work->parent->set_dirty(parent_bit);
|
||||
|
||||
if (verbose)
|
||||
log(" Propagating %s.%s[%d] -> %s.%s[%d].\n", work->log_prefix.c_str(), log_id(bit.wire), bit.offset,
|
||||
work->parent->log_prefix.c_str(), log_id(parent_bit.wire), parent_bit.offset);
|
||||
}
|
||||
|
||||
for (auto &port : bit2cell[work->module][bit])
|
||||
{
|
||||
if (work->children.count(std::get<0>(port)->name))
|
||||
{
|
||||
HierDirtyFlags *child = work->children.at(std::get<0>(port)->name);
|
||||
SigBit child_bit = sigmaps.at(child->module)(SigBit(child->module->wire(std::get<1>(port)), std::get<2>(port)));
|
||||
log_assert(bit.wire && child_bit.wire);
|
||||
|
||||
funct_declarations.push_back(util_set_bit(work->prefix + cid(child->hiername) + "." + cid(child_bit.wire->name),
|
||||
child_bit.wire->width, child_bit.offset, util_get_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset)));
|
||||
child->set_dirty(child_bit);
|
||||
|
||||
if (verbose)
|
||||
log(" Propagating %s.%s[%d] -> %s.%s.%s[%d].\n", work->log_prefix.c_str(), log_id(bit.wire), bit.offset,
|
||||
work->log_prefix.c_str(), log_id(std::get<0>(port)), log_id(child_bit.wire), child_bit.offset);
|
||||
} else {
|
||||
if (verbose)
|
||||
log(" Marking cell %s.%s (via %s.%s[%d]).\n", work->log_prefix.c_str(), log_id(std::get<0>(port)),
|
||||
work->log_prefix.c_str(), log_id(bit.wire), bit.offset);
|
||||
work->set_dirty(std::get<0>(port));
|
||||
}
|
||||
}
|
||||
work->unset_dirty(bit);
|
||||
}
|
||||
}
|
||||
|
||||
if (!work->dirty_cells.empty())
|
||||
{
|
||||
Cell *cell = nullptr;
|
||||
for (auto c : work->dirty_cells)
|
||||
if (cell == nullptr || topoidx.at(cell) < topoidx.at(c))
|
||||
cell = c;
|
||||
|
||||
string hiername = work->log_prefix + "." + log_id(cell);
|
||||
|
||||
if (verbose)
|
||||
log(" Evaluating %s (%s, best of %d).\n", hiername.c_str(), log_id(cell->type), GetSize(work->dirty_cells));
|
||||
|
||||
if (activated_cells.count(hiername))
|
||||
reactivated_cells.insert(hiername);
|
||||
activated_cells.insert(hiername);
|
||||
|
||||
eval_cell(work, cell);
|
||||
work->unset_dirty(cell);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &child : work->children)
|
||||
eval_dirty(child.second);
|
||||
}
|
||||
}
|
||||
|
||||
void eval_sticky_dirty(HierDirtyFlags *work)
|
||||
{
|
||||
Module *mod = work->module;
|
||||
|
||||
for (Wire *w : mod->wires())
|
||||
for (SigBit bit : SigSpec(w))
|
||||
{
|
||||
SigBit canonical_bit = sigmaps.at(mod)(bit);
|
||||
|
||||
if (canonical_bit == bit)
|
||||
continue;
|
||||
|
||||
if (work->sticky_dirty_bits.count(canonical_bit) == 0)
|
||||
continue;
|
||||
|
||||
if (bit.wire == nullptr || canonical_bit.wire == nullptr)
|
||||
continue;
|
||||
|
||||
funct_declarations.push_back(util_set_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset,
|
||||
util_get_bit(work->prefix + cid(canonical_bit.wire->name), canonical_bit.wire->width, canonical_bit.offset).c_str()));
|
||||
|
||||
if (verbose)
|
||||
log(" Propagating alias %s.%s[%d] -> %s.%s[%d].\n",
|
||||
work->log_prefix.c_str(), log_id(canonical_bit.wire), canonical_bit.offset,
|
||||
work->log_prefix.c_str(), log_id(bit.wire), bit.offset);
|
||||
}
|
||||
|
||||
work->sticky_dirty_bits.clear();
|
||||
|
||||
for (auto &child : work->children)
|
||||
eval_sticky_dirty(child.second);
|
||||
}
|
||||
|
||||
void make_func(HierDirtyFlags *work, const string &func_name, const vector<string> &preamble)
|
||||
{
|
||||
log("Generating function %s():\n", func_name.c_str());
|
||||
|
||||
activated_cells.clear();
|
||||
reactivated_cells.clear();
|
||||
|
||||
funct_declarations.push_back("");
|
||||
funct_declarations.push_back(stringf("static void %s(struct %s_state_t *state)", func_name.c_str(), cid(work->module->name).c_str()));
|
||||
funct_declarations.push_back("{");
|
||||
for (auto &line : preamble)
|
||||
funct_declarations.push_back(line);
|
||||
eval_dirty(work);
|
||||
eval_sticky_dirty(work);
|
||||
funct_declarations.push_back("}");
|
||||
|
||||
log(" Activated %d cells (%d activated more than once).\n", GetSize(activated_cells), GetSize(reactivated_cells));
|
||||
}
|
||||
|
||||
void eval_init(HierDirtyFlags *work, vector<string> &preamble)
|
||||
{
|
||||
Module *module = work->module;
|
||||
|
||||
for (Wire *w : module->wires())
|
||||
{
|
||||
if (w->attributes.count("\\init"))
|
||||
{
|
||||
SigSpec sig = sigmaps.at(module)(w);
|
||||
Const val = w->attributes.at("\\init");
|
||||
val.bits.resize(GetSize(sig), State::Sx);
|
||||
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
if (val[i] == State::S0 || val[i] == State::S1) {
|
||||
SigBit bit = sig[i];
|
||||
preamble.push_back(util_set_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset, val == State::S1 ? "true" : "false"));
|
||||
work->set_dirty(bit);
|
||||
}
|
||||
}
|
||||
|
||||
for (SigBit bit : SigSpec(w))
|
||||
{
|
||||
SigBit val = sigmaps.at(module)(bit);
|
||||
|
||||
if (val == State::S0 || val == State::S1)
|
||||
preamble.push_back(util_set_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset, val == State::S1 ? "true" : "false"));
|
||||
|
||||
if (driven_bits.at(module).count(val) == 0)
|
||||
work->set_dirty(val);
|
||||
}
|
||||
}
|
||||
|
||||
work->set_dirty(State::S0);
|
||||
work->set_dirty(State::S1);
|
||||
|
||||
for (auto &child : work->children)
|
||||
eval_init(child.second, preamble);
|
||||
}
|
||||
|
||||
void make_init_func(HierDirtyFlags *work)
|
||||
{
|
||||
vector<string> preamble;
|
||||
eval_init(work, preamble);
|
||||
make_func(work, cid(work->module->name) + "_init", preamble);
|
||||
}
|
||||
|
||||
void make_eval_func(HierDirtyFlags *work)
|
||||
{
|
||||
Module *mod = work->module;
|
||||
vector<string> preamble;
|
||||
|
||||
for (Wire *w : mod->wires()) {
|
||||
if (w->port_input)
|
||||
for (SigBit bit : sigmaps.at(mod)(w))
|
||||
work->set_dirty(bit);
|
||||
}
|
||||
|
||||
make_func(work, cid(work->module->name) + "_eval", preamble);
|
||||
}
|
||||
|
||||
void make_tick_func(HierDirtyFlags* /* work */)
|
||||
{
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void run(Module *mod)
|
||||
{
|
||||
create_module_struct(mod);
|
||||
|
||||
HierDirtyFlags work(mod, IdString(), nullptr, "state->", log_id(mod->name));
|
||||
|
||||
make_init_func(&work);
|
||||
make_eval_func(&work);
|
||||
make_tick_func(&work);
|
||||
}
|
||||
|
||||
void write(std::ostream &f)
|
||||
{
|
||||
f << "#include <stdint.h>" << std::endl;
|
||||
f << "#include <stdbool.h>" << std::endl;
|
||||
|
||||
for (auto &line : signal_declarations)
|
||||
f << line << std::endl;
|
||||
|
||||
for (auto &line : util_declarations)
|
||||
f << line << std::endl;
|
||||
|
||||
for (auto &line : struct_declarations)
|
||||
f << line << std::endl;
|
||||
|
||||
for (auto &line : funct_declarations)
|
||||
f << line << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
struct SimplecBackend : public Backend {
|
||||
SimplecBackend() : Backend("simplec", "convert design to simple C code") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" write_simplec [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Write simple C code for simulating the design. The C code written can be used to\n");
|
||||
log("simulate the design in a C environment, but the purpose of this command is to\n");
|
||||
log("generate code that works well with C-based formal verification.\n");
|
||||
log("\n");
|
||||
log(" -verbose\n");
|
||||
log(" this will print the recursive walk used to export the modules.\n");
|
||||
log("\n");
|
||||
log(" -i8, -i16, -i32, -i64\n");
|
||||
log(" set the maximum integer bit width to use in the generated code.\n");
|
||||
log("\n");
|
||||
log("THIS COMMAND IS UNDER CONSTRUCTION\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
reserved_cids.clear();
|
||||
id2cid.clear();
|
||||
|
||||
SimplecWorker worker(design);
|
||||
|
||||
log_header(design, "Executing SIMPLEC backend.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-verbose") {
|
||||
worker.verbose = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-i8") {
|
||||
worker.max_uintsize = 8;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-i16") {
|
||||
worker.max_uintsize = 16;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-i32") {
|
||||
worker.max_uintsize = 32;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-i64") {
|
||||
worker.max_uintsize = 64;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
Module *topmod = design->top_module();
|
||||
|
||||
if (topmod == nullptr)
|
||||
log_error("Current design has no top module.\n");
|
||||
|
||||
worker.run(topmod);
|
||||
worker.write(*f);
|
||||
}
|
||||
} SimplecBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
set -ex
|
||||
../../yosys -p 'synth -top test; write_simplec -verbose -i8 test00_uut.c' test00_uut.v
|
||||
clang -o test00_tb test00_tb.c
|
||||
./test00_tb
|
|
@ -0,0 +1,93 @@
|
|||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include "test00_uut.c"
|
||||
|
||||
uint32_t xorshift32()
|
||||
{
|
||||
static uint32_t x32 = 314159265;
|
||||
x32 ^= x32 << 13;
|
||||
x32 ^= x32 >> 17;
|
||||
x32 ^= x32 << 5;
|
||||
return x32;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
struct test_state_t state;
|
||||
uint32_t a, b, c, x, y, z, w;
|
||||
bool first_eval = true;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
a = xorshift32();
|
||||
b = xorshift32();
|
||||
c = xorshift32();
|
||||
|
||||
x = (a & b) | c;
|
||||
y = a & (b | c);
|
||||
z = a ^ b ^ c;
|
||||
w = z;
|
||||
|
||||
state.a.value_7_0 = a;
|
||||
state.a.value_15_8 = a >> 8;
|
||||
state.a.value_23_16 = a >> 16;
|
||||
state.a.value_31_24 = a >> 24;
|
||||
|
||||
state.b.value_7_0 = b;
|
||||
state.b.value_15_8 = b >> 8;
|
||||
state.b.value_23_16 = b >> 16;
|
||||
state.b.value_31_24 = b >> 24;
|
||||
|
||||
state.c.value_7_0 = c;
|
||||
state.c.value_15_8 = c >> 8;
|
||||
state.c.value_23_16 = c >> 16;
|
||||
state.c.value_31_24 = c >> 24;
|
||||
|
||||
if (first_eval) {
|
||||
first_eval = false;
|
||||
test_init(&state);
|
||||
} else {
|
||||
test_eval(&state);
|
||||
}
|
||||
|
||||
uint32_t uut_x = 0;
|
||||
uut_x |= (uint32_t)state.x.value_7_0;
|
||||
uut_x |= (uint32_t)state.x.value_15_8 << 8;
|
||||
uut_x |= (uint32_t)state.x.value_23_16 << 16;
|
||||
uut_x |= (uint32_t)state.x.value_31_24 << 24;
|
||||
|
||||
uint32_t uut_y = 0;
|
||||
uut_y |= (uint32_t)state.y.value_7_0;
|
||||
uut_y |= (uint32_t)state.y.value_15_8 << 8;
|
||||
uut_y |= (uint32_t)state.y.value_23_16 << 16;
|
||||
uut_y |= (uint32_t)state.y.value_31_24 << 24;
|
||||
|
||||
uint32_t uut_z = 0;
|
||||
uut_z |= (uint32_t)state.z.value_7_0;
|
||||
uut_z |= (uint32_t)state.z.value_15_8 << 8;
|
||||
uut_z |= (uint32_t)state.z.value_23_16 << 16;
|
||||
uut_z |= (uint32_t)state.z.value_31_24 << 24;
|
||||
|
||||
uint32_t uut_w = 0;
|
||||
uut_w |= (uint32_t)state.w.value_7_0;
|
||||
uut_w |= (uint32_t)state.w.value_15_8 << 8;
|
||||
uut_w |= (uint32_t)state.w.value_23_16 << 16;
|
||||
uut_w |= (uint32_t)state.w.value_31_24 << 24;
|
||||
|
||||
printf("---\n");
|
||||
printf("A: 0x%08x\n", a);
|
||||
printf("B: 0x%08x\n", b);
|
||||
printf("C: 0x%08x\n", c);
|
||||
printf("X: 0x%08x 0x%08x\n", x, uut_x);
|
||||
printf("Y: 0x%08x 0x%08x\n", y, uut_y);
|
||||
printf("Z: 0x%08x 0x%08x\n", z, uut_z);
|
||||
printf("W: 0x%08x 0x%08x\n", w, uut_w);
|
||||
|
||||
assert(x == uut_x);
|
||||
assert(y == uut_y);
|
||||
assert(z == uut_z);
|
||||
assert(w == uut_w);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
module test(input [31:0] a, b, c, output [31:0] x, y, z, w);
|
||||
unit_x unit_x_inst (.a(a), .b(b), .c(c), .x(x));
|
||||
unit_y unit_y_inst (.a(a), .b(b), .c(c), .y(y));
|
||||
assign z = a ^ b ^ c, w = z;
|
||||
endmodule
|
||||
|
||||
module unit_x(input [31:0] a, b, c, output [31:0] x);
|
||||
assign x = (a & b) | c;
|
||||
endmodule
|
||||
|
||||
module unit_y(input [31:0] a, b, c, output [31:0] y);
|
||||
assign y = a & (b | c);
|
||||
endmodule
|
||||
|
|
@ -6,7 +6,7 @@ ifneq ($(CONFIG),emcc)
|
|||
TARGETS += yosys-smtbmc
|
||||
|
||||
yosys-smtbmc: backends/smt2/smtbmc.py
|
||||
$(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(__file__) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new
|
||||
$(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new
|
||||
$(Q) chmod +x $@.new
|
||||
$(Q) mv $@.new $@
|
||||
|
||||
|
|
|
@ -32,14 +32,18 @@ struct Smt2Worker
|
|||
CellTypes ct;
|
||||
SigMap sigmap;
|
||||
RTLIL::Module *module;
|
||||
bool bvmode, memmode, wiresmode, verbose;
|
||||
int idcounter;
|
||||
bool bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode;
|
||||
dict<IdString, int> &mod_stbv_width;
|
||||
int idcounter = 0, statebv_width = 0;
|
||||
|
||||
std::vector<std::string> decls, trans, hier;
|
||||
std::vector<std::string> decls, trans, hier, dtmembers;
|
||||
std::map<RTLIL::SigBit, RTLIL::Cell*> bit_driver;
|
||||
std::set<RTLIL::Cell*> exported_cells, hiercells, hiercells_queue;
|
||||
pool<Cell*> recursive_cells, registers;
|
||||
|
||||
pool<SigBit> clock_posedge, clock_negedge;
|
||||
vector<string> ex_state_eq, ex_input_eq;
|
||||
|
||||
std::map<RTLIL::SigBit, std::pair<int, int>> fcache;
|
||||
std::map<Cell*, int> memarrays;
|
||||
std::map<int, int> bvsizes;
|
||||
|
@ -63,17 +67,64 @@ struct Smt2Worker
|
|||
return get_id(obj->name);
|
||||
}
|
||||
|
||||
Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool wiresmode, bool verbose) :
|
||||
ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode),
|
||||
wiresmode(wiresmode), verbose(verbose), idcounter(0)
|
||||
void makebits(std::string name, int width = 0, std::string comment = std::string())
|
||||
{
|
||||
decls.push_back(stringf("(declare-sort |%s_s| 0)\n", get_id(module)));
|
||||
decls.push_back(stringf("(declare-fun |%s_is| (|%s_s|) Bool)\n", get_id(module), get_id(module)));
|
||||
std::string decl_str;
|
||||
|
||||
if (statebv)
|
||||
{
|
||||
if (width == 0) {
|
||||
decl_str = stringf("(define-fun |%s| ((state |%s_s|)) Bool (= ((_ extract %d %d) state) #b1))", name.c_str(), get_id(module), statebv_width, statebv_width);
|
||||
statebv_width += 1;
|
||||
} else {
|
||||
decl_str = stringf("(define-fun |%s| ((state |%s_s|)) (_ BitVec %d) ((_ extract %d %d) state))", name.c_str(), get_id(module), width, statebv_width+width-1, statebv_width);
|
||||
statebv_width += width;
|
||||
}
|
||||
}
|
||||
else if (statedt)
|
||||
{
|
||||
if (width == 0) {
|
||||
decl_str = stringf(" (|%s| Bool)", name.c_str());
|
||||
} else {
|
||||
decl_str = stringf(" (|%s| (_ BitVec %d))", name.c_str(), width);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (width == 0) {
|
||||
decl_str = stringf("(declare-fun |%s| (|%s_s|) Bool)", name.c_str(), get_id(module));
|
||||
} else {
|
||||
decl_str = stringf("(declare-fun |%s| (|%s_s|) (_ BitVec %d))", name.c_str(), get_id(module), width);
|
||||
}
|
||||
}
|
||||
|
||||
if (!comment.empty())
|
||||
decl_str += " ; " + comment;
|
||||
|
||||
if (statedt)
|
||||
dtmembers.push_back(decl_str + "\n");
|
||||
else
|
||||
decls.push_back(decl_str + "\n");
|
||||
}
|
||||
|
||||
Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool wiresmode, bool verbose, bool statebv, bool statedt, bool forallmode,
|
||||
dict<IdString, int> &mod_stbv_width, dict<IdString, dict<IdString, pair<bool, bool>>> &mod_clk_cache) :
|
||||
ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode), wiresmode(wiresmode),
|
||||
verbose(verbose), statebv(statebv), statedt(statedt), forallmode(forallmode), mod_stbv_width(mod_stbv_width)
|
||||
{
|
||||
pool<SigBit> noclock;
|
||||
|
||||
makebits(stringf("%s_is", get_id(module)));
|
||||
|
||||
for (auto cell : module->cells())
|
||||
for (auto &conn : cell->connections()) {
|
||||
for (auto &conn : cell->connections())
|
||||
{
|
||||
if (GetSize(conn.second) == 0)
|
||||
continue;
|
||||
|
||||
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))
|
||||
|
@ -83,6 +134,66 @@ struct Smt2Worker
|
|||
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));
|
||||
|
||||
if (cell->type.in("$mem") && conn.first.in("\\RD_CLK", "\\WR_CLK"))
|
||||
{
|
||||
SigSpec clk = sigmap(conn.second);
|
||||
for (int i = 0; i < GetSize(clk); i++)
|
||||
{
|
||||
if (clk[i].wire == nullptr)
|
||||
continue;
|
||||
|
||||
if (cell->getParam(conn.first == "\\RD_CLK" ? "\\RD_CLK_ENABLE" : "\\WR_CLK_ENABLE")[i] != State::S1)
|
||||
continue;
|
||||
|
||||
if (cell->getParam(conn.first == "\\RD_CLK" ? "\\RD_CLK_POLARITY" : "\\WR_CLK_POLARITY")[i] == State::S1)
|
||||
clock_posedge.insert(clk[i]);
|
||||
else
|
||||
clock_negedge.insert(clk[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_") && conn.first.in("\\CLK", "\\C"))
|
||||
{
|
||||
bool posedge = (cell->type == "$_DFF_N_") || (cell->type == "$dff" && cell->getParam("\\CLK_POLARITY").as_bool());
|
||||
for (auto bit : sigmap(conn.second)) {
|
||||
if (posedge)
|
||||
clock_posedge.insert(bit);
|
||||
else
|
||||
clock_negedge.insert(bit);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (mod_clk_cache.count(cell->type) && mod_clk_cache.at(cell->type).count(conn.first))
|
||||
{
|
||||
for (auto bit : sigmap(conn.second)) {
|
||||
if (mod_clk_cache.at(cell->type).at(conn.first).first)
|
||||
clock_posedge.insert(bit);
|
||||
if (mod_clk_cache.at(cell->type).at(conn.first).second)
|
||||
clock_negedge.insert(bit);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto bit : sigmap(conn.second))
|
||||
noclock.insert(bit);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto bit : noclock) {
|
||||
clock_posedge.erase(bit);
|
||||
clock_negedge.erase(bit);
|
||||
}
|
||||
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
if (!wire->port_input || GetSize(wire) != 1)
|
||||
continue;
|
||||
SigBit bit = sigmap(wire);
|
||||
if (clock_posedge.count(bit))
|
||||
mod_clk_cache[module->name][wire->name].first = true;
|
||||
if (clock_negedge.count(bit))
|
||||
mod_clk_cache[module->name][wire->name].second = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,8 +273,7 @@ struct Smt2Worker
|
|||
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",
|
||||
get_id(module), idcounter, get_id(module), log_signal(bit)));
|
||||
makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(bit));
|
||||
register_bool(bit, idcounter++);
|
||||
}
|
||||
|
||||
|
@ -237,8 +347,7 @@ struct Smt2Worker
|
|||
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",
|
||||
get_id(module), idcounter, get_id(module), j, log_signal(sig.extract(i, j))));
|
||||
makebits(stringf("%s#%d", get_id(module), idcounter), j, log_signal(sig.extract(i, j)));
|
||||
subexpr.push_back(stringf("(|%s#%d| %s)", get_id(module), idcounter, state_name));
|
||||
register_bv(sig.extract(i, j), idcounter++);
|
||||
}
|
||||
|
@ -288,6 +397,7 @@ struct Smt2Worker
|
|||
|
||||
if (type == 's' || type == 'd' || type == 'b') {
|
||||
width = max(width, GetSize(cell->getPort("\\A")));
|
||||
if (cell->hasPort("\\B"))
|
||||
width = max(width, GetSize(cell->getPort("\\B")));
|
||||
}
|
||||
|
||||
|
@ -382,8 +492,7 @@ struct Smt2Worker
|
|||
if (cell->type.in("$_FF_", "$_DFF_P_", "$_DFF_N_"))
|
||||
{
|
||||
registers.insert(cell);
|
||||
decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n",
|
||||
get_id(module), idcounter, get_id(module), log_signal(cell->getPort("\\Q"))));
|
||||
makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(cell->getPort("\\Q")));
|
||||
register_bool(cell->getPort("\\Q"), idcounter++);
|
||||
recursive_cells.erase(cell);
|
||||
return;
|
||||
|
@ -397,6 +506,8 @@ struct Smt2Worker
|
|||
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 == "$_ANDNOT_") return export_gate(cell, "(and A (not B))");
|
||||
if (cell->type == "$_ORNOT_") return export_gate(cell, "(or A (not 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))");
|
||||
|
@ -410,20 +521,22 @@ struct Smt2Worker
|
|||
if (cell->type.in("$ff", "$dff"))
|
||||
{
|
||||
registers.insert(cell);
|
||||
decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n",
|
||||
get_id(module), idcounter, get_id(module), GetSize(cell->getPort("\\Q")), log_signal(cell->getPort("\\Q"))));
|
||||
makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort("\\Q")), log_signal(cell->getPort("\\Q")));
|
||||
register_bv(cell->getPort("\\Q"), idcounter++);
|
||||
recursive_cells.erase(cell);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell->type.in("$anyconst", "$anyseq"))
|
||||
if (cell->type.in("$anyconst", "$anyseq", "$allconst", "$allseq"))
|
||||
{
|
||||
registers.insert(cell);
|
||||
decls.push_back(stringf("; yosys-smt2-%s %s#%d %s\n", cell->type.c_str() + 1, get_id(module), idcounter,
|
||||
cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell)));
|
||||
decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n",
|
||||
get_id(module), idcounter, get_id(module), GetSize(cell->getPort("\\Y")), log_signal(cell->getPort("\\Y"))));
|
||||
string infostr = cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell);
|
||||
if (cell->attributes.count("\\reg"))
|
||||
infostr += " " + cell->attributes.at("\\reg").decode_string();
|
||||
decls.push_back(stringf("; yosys-smt2-%s %s#%d %d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, GetSize(cell->getPort("\\Y")), infostr.c_str()));
|
||||
makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort("\\Y")), log_signal(cell->getPort("\\Y")));
|
||||
if (cell->type == "$anyseq")
|
||||
ex_input_eq.push_back(stringf(" (= (|%s#%d| state) (|%s#%d| other_state))", get_id(module), idcounter, get_id(module), idcounter));
|
||||
register_bv(cell->getPort("\\Y"), idcounter++);
|
||||
recursive_cells.erase(cell);
|
||||
return;
|
||||
|
@ -441,7 +554,9 @@ struct Smt2Worker
|
|||
|
||||
if (cell->type.in("$shift", "$shiftx")) {
|
||||
if (cell->getParam("\\B_SIGNED").as_bool()) {
|
||||
/* FIXME */
|
||||
return export_bvop(cell, stringf("(ite (bvsge B #b%0*d) "
|
||||
"(bvlshr A B) (bvlshr A (bvneg B)))",
|
||||
GetSize(cell->getPort("\\B")), 0), 's');
|
||||
} else {
|
||||
return export_bvop(cell, "(bvlshr A B)", 's');
|
||||
}
|
||||
|
@ -467,6 +582,13 @@ struct Smt2Worker
|
|||
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.in("$reduce_and", "$reduce_or", "$reduce_bool") &&
|
||||
2*GetSize(cell->getPort("\\A").chunks()) < GetSize(cell->getPort("\\A"))) {
|
||||
bool is_and = cell->type == "$reduce_and";
|
||||
string bits(GetSize(cell->getPort("\\A")), is_and ? '1' : '0');
|
||||
return export_bvop(cell, stringf("(%s A #b%s)", is_and ? "=" : "distinct", bits.c_str()), 'b');
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -513,13 +635,32 @@ struct Smt2Worker
|
|||
int abits = cell->getParam("\\ABITS").as_int();
|
||||
int width = cell->getParam("\\WIDTH").as_int();
|
||||
int rd_ports = cell->getParam("\\RD_PORTS").as_int();
|
||||
int wr_ports = cell->getParam("\\WR_PORTS").as_int();
|
||||
|
||||
decls.push_back(stringf("(declare-fun |%s#%d#0| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
|
||||
get_id(module), arrayid, get_id(module), abits, width, get_id(cell)));
|
||||
bool async_read = false;
|
||||
if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_ones()) {
|
||||
if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_zero())
|
||||
log_error("Memory %s.%s has mixed clocked/nonclocked write ports. This is not supported by \"write_smt2\".\n", log_id(cell), log_id(module));
|
||||
async_read = true;
|
||||
}
|
||||
|
||||
decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d\n", get_id(cell), abits, width, rd_ports));
|
||||
decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s#%d#0| state))\n",
|
||||
get_id(module), get_id(cell), get_id(module), abits, width, get_id(module), arrayid));
|
||||
decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d %s\n", get_id(cell), abits, width, rd_ports, wr_ports, async_read ? "async" : "sync"));
|
||||
|
||||
string memstate;
|
||||
if (async_read) {
|
||||
memstate = stringf("%s#%d#final", get_id(module), arrayid);
|
||||
} else {
|
||||
memstate = stringf("%s#%d#0", get_id(module), arrayid);
|
||||
}
|
||||
|
||||
if (statebv)
|
||||
{
|
||||
int mem_size = cell->getParam("\\SIZE").as_int();
|
||||
int mem_offset = cell->getParam("\\OFFSET").as_int();
|
||||
|
||||
makebits(memstate, width*mem_size, get_id(cell));
|
||||
decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (_ BitVec %d) (|%s| state))\n",
|
||||
get_id(module), get_id(cell), get_id(module), width*mem_size, memstate.c_str()));
|
||||
|
||||
for (int i = 0; i < rd_ports; i++)
|
||||
{
|
||||
|
@ -531,13 +672,61 @@ struct Smt2Worker
|
|||
log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! "
|
||||
"Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(data_sig), log_id(cell), log_id(module));
|
||||
|
||||
decls.push_back(stringf("(define-fun |%s_m:%d %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
|
||||
decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
|
||||
get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
|
||||
|
||||
decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s#%d#0| state) %s)) ; %s\n",
|
||||
get_id(module), idcounter, get_id(module), width, get_id(module), arrayid, addr.c_str(), log_signal(data_sig)));
|
||||
std::string read_expr = "#b";
|
||||
for (int k = 0; k < width; k++)
|
||||
read_expr += "0";
|
||||
|
||||
for (int k = 0; k < mem_size; k++)
|
||||
read_expr = stringf("(ite (= (|%s_m:R%dA %s| state) #b%s) ((_ extract %d %d) (|%s| state))\n %s)",
|
||||
get_id(module), i, get_id(cell), Const(k+mem_offset, abits).as_string().c_str(),
|
||||
width*(k+1)-1, width*k, memstate.c_str(), read_expr.c_str());
|
||||
|
||||
decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d)\n %s) ; %s\n",
|
||||
get_id(module), idcounter, get_id(module), width, read_expr.c_str(), log_signal(data_sig)));
|
||||
|
||||
decls.push_back(stringf("(define-fun |%s_m:R%dD %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d| state))\n",
|
||||
get_id(module), i, get_id(cell), get_id(module), width, get_id(module), idcounter));
|
||||
|
||||
register_bv(data_sig, idcounter++);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (statedt)
|
||||
dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
|
||||
memstate.c_str(), abits, width, get_id(cell)));
|
||||
else
|
||||
decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
|
||||
memstate.c_str(), get_id(module), abits, width, get_id(cell)));
|
||||
|
||||
decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s| state))\n",
|
||||
get_id(module), get_id(cell), get_id(module), abits, width, memstate.c_str()));
|
||||
|
||||
for (int i = 0; i < rd_ports; i++)
|
||||
{
|
||||
SigSpec addr_sig = cell->getPort("\\RD_ADDR").extract(abits*i, abits);
|
||||
SigSpec data_sig = cell->getPort("\\RD_DATA").extract(width*i, width);
|
||||
std::string addr = get_bv(addr_sig);
|
||||
|
||||
if (cell->getParam("\\RD_CLK_ENABLE").extract(i).as_bool())
|
||||
log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! "
|
||||
"Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(data_sig), log_id(cell), log_id(module));
|
||||
|
||||
decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
|
||||
get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
|
||||
|
||||
decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s| state) (|%s_m:R%dA %s| state))) ; %s\n",
|
||||
get_id(module), idcounter, get_id(module), width, memstate.c_str(), get_id(module), i, get_id(cell), log_signal(data_sig)));
|
||||
|
||||
decls.push_back(stringf("(define-fun |%s_m:R%dD %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d| state))\n",
|
||||
get_id(module), i, get_id(cell), get_id(module), width, get_id(module), idcounter));
|
||||
|
||||
register_bv(data_sig, idcounter++);
|
||||
}
|
||||
}
|
||||
|
||||
registers.insert(cell);
|
||||
recursive_cells.erase(cell);
|
||||
|
@ -553,30 +742,36 @@ struct Smt2Worker
|
|||
|
||||
for (auto &conn : cell->connections())
|
||||
{
|
||||
if (GetSize(conn.second) == 0)
|
||||
continue;
|
||||
|
||||
Wire *w = m->wire(conn.first);
|
||||
SigSpec sig = sigmap(conn.second);
|
||||
|
||||
if (w->port_output && !w->port_input) {
|
||||
if (GetSize(w) > 1) {
|
||||
if (bvmode) {
|
||||
decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n",
|
||||
get_id(module), idcounter, get_id(module), GetSize(w), log_signal(sig)));
|
||||
makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(w), log_signal(sig));
|
||||
register_bv(sig, idcounter++);
|
||||
} else {
|
||||
for (int i = 0; i < GetSize(w); i++) {
|
||||
decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n",
|
||||
get_id(module), idcounter, get_id(module), log_signal(sig[i])));
|
||||
makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(sig[i]));
|
||||
register_bool(sig[i], idcounter++);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n",
|
||||
get_id(module), idcounter, get_id(module), log_signal(sig)));
|
||||
makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(sig));
|
||||
register_bool(sig, idcounter++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (statebv)
|
||||
makebits(stringf("%s_h %s", get_id(module), get_id(cell->name)), mod_stbv_width.at(cell->type));
|
||||
else if (statedt)
|
||||
dtmembers.push_back(stringf(" (|%s_h %s| |%s_s|)\n",
|
||||
get_id(module), get_id(cell->name), get_id(cell->type)));
|
||||
else
|
||||
decls.push_back(stringf("(declare-fun |%s_h %s| (|%s_s|) |%s_s|)\n",
|
||||
get_id(module), get_id(cell->name), get_id(module), get_id(cell->type)));
|
||||
|
||||
|
@ -617,17 +812,30 @@ struct Smt2Worker
|
|||
decls.push_back(stringf("; yosys-smt2-register %s %d\n", get_id(wire), wire->width));
|
||||
if (wire->get_bool_attribute("\\keep") || (wiresmode && wire->name[0] == '\\'))
|
||||
decls.push_back(stringf("; yosys-smt2-wire %s %d\n", get_id(wire), wire->width));
|
||||
if (GetSize(wire) == 1 && (clock_posedge.count(sig) || clock_negedge.count(sig)))
|
||||
decls.push_back(stringf("; yosys-smt2-clock %s%s%s\n", get_id(wire),
|
||||
clock_posedge.count(sig) ? " posedge" : "", clock_negedge.count(sig) ? " negedge" : ""));
|
||||
if (bvmode && GetSize(sig) > 1) {
|
||||
decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) (_ BitVec %d) %s)\n",
|
||||
get_id(module), get_id(wire), get_id(module), GetSize(sig), get_bv(sig).c_str()));
|
||||
if (wire->port_input)
|
||||
ex_input_eq.push_back(stringf(" (= (|%s_n %s| state) (|%s_n %s| other_state))",
|
||||
get_id(module), get_id(wire), get_id(module), get_id(wire)));
|
||||
} else {
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
if (GetSize(sig) > 1)
|
||||
if (GetSize(sig) > 1) {
|
||||
decls.push_back(stringf("(define-fun |%s_n %s %d| ((state |%s_s|)) Bool %s)\n",
|
||||
get_id(module), get_id(wire), i, get_id(module), get_bool(sig[i]).c_str()));
|
||||
else
|
||||
if (wire->port_input)
|
||||
ex_input_eq.push_back(stringf(" (= (|%s_n %s %d| state) (|%s_n %s %d| other_state))",
|
||||
get_id(module), get_id(wire), i, get_id(module), get_id(wire), i));
|
||||
} else {
|
||||
decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) Bool %s)\n",
|
||||
get_id(module), get_id(wire), get_id(module), get_bool(sig[i]).c_str()));
|
||||
if (wire->port_input)
|
||||
ex_input_eq.push_back(stringf(" (= (|%s_n %s| state) (|%s_n %s| other_state))",
|
||||
get_id(module), get_id(wire), get_id(module), get_id(wire)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -639,30 +847,103 @@ struct Smt2Worker
|
|||
if (wire->attributes.count("\\init")) {
|
||||
RTLIL::SigSpec sig = sigmap(wire);
|
||||
Const val = wire->attributes.at("\\init");
|
||||
val.bits.resize(GetSize(sig));
|
||||
val.bits.resize(GetSize(sig), State::Sx);
|
||||
if (bvmode && GetSize(sig) > 1) {
|
||||
Const mask(State::S1, GetSize(sig));
|
||||
bool use_mask = false;
|
||||
for (int i = 0; i < GetSize(sig); i++)
|
||||
if (val[i] != State::S0 && val[i] != State::S1) {
|
||||
val[i] = State::S0;
|
||||
mask[i] = State::S0;
|
||||
use_mask = true;
|
||||
}
|
||||
if (use_mask)
|
||||
init_list.push_back(stringf("(= (bvand %s #b%s) #b%s) ; %s", get_bv(sig).c_str(), mask.as_string().c_str(), val.as_string().c_str(), get_id(wire)));
|
||||
else
|
||||
init_list.push_back(stringf("(= %s #b%s) ; %s", get_bv(sig).c_str(), val.as_string().c_str(), get_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", get_id(wire)));
|
||||
if (val[i] == State::S0 || val[i] == State::S1)
|
||||
init_list.push_back(stringf("(= %s %s) ; %s", get_bool(sig[i]).c_str(), val[i] == State::S1 ? "true" : "false", get_id(wire)));
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose) log("=> export logic driving asserts\n");
|
||||
|
||||
vector<string> assert_list, assume_list;
|
||||
int assert_id = 0, assume_id = 0, cover_id = 0;
|
||||
vector<string> assert_list, assume_list, cover_list;
|
||||
|
||||
for (auto cell : module->cells())
|
||||
if (cell->type.in("$assert", "$assume")) {
|
||||
{
|
||||
if (cell->type.in("$assert", "$assume", "$cover"))
|
||||
{
|
||||
int &id = cell->type == "$assert" ? assert_id :
|
||||
cell->type == "$assume" ? assume_id :
|
||||
cell->type == "$cover" ? cover_id : *(int*)nullptr;
|
||||
|
||||
char postfix = cell->type == "$assert" ? 'a' :
|
||||
cell->type == "$assume" ? 'u' :
|
||||
cell->type == "$cover" ? 'c' : 0;
|
||||
|
||||
string name_a = get_bool(cell->getPort("\\A"));
|
||||
string name_en = get_bool(cell->getPort("\\EN"));
|
||||
decls.push_back(stringf("; yosys-smt2-%s %s#%d %s\n", cell->type.c_str() + 1, get_id(module), idcounter,
|
||||
decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id,
|
||||
cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell)));
|
||||
decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool (or %s (not %s))) ; %s\n",
|
||||
get_id(module), idcounter, get_id(module), name_a.c_str(), name_en.c_str(), get_id(cell)));
|
||||
if (cell->type == "$assert")
|
||||
assert_list.push_back(stringf("(|%s#%d| state)", get_id(module), idcounter++));
|
||||
|
||||
if (cell->type == "$cover")
|
||||
decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (and %s %s)) ; %s\n",
|
||||
get_id(module), postfix, id, get_id(module), name_a.c_str(), name_en.c_str(), get_id(cell)));
|
||||
else
|
||||
assume_list.push_back(stringf("(|%s#%d| state)", get_id(module), idcounter++));
|
||||
decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (or %s (not %s))) ; %s\n",
|
||||
get_id(module), postfix, id, get_id(module), name_a.c_str(), name_en.c_str(), get_id(cell)));
|
||||
|
||||
if (cell->type == "$assert")
|
||||
assert_list.push_back(stringf("(|%s_a %d| state)", get_id(module), id));
|
||||
else if (cell->type == "$assume")
|
||||
assume_list.push_back(stringf("(|%s_u %d| state)", get_id(module), id));
|
||||
|
||||
id++;
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose) log("=> export logic driving hierarchical cells\n");
|
||||
|
||||
for (auto cell : module->cells())
|
||||
if (module->design->module(cell->type) != nullptr)
|
||||
export_cell(cell);
|
||||
|
||||
while (!hiercells_queue.empty())
|
||||
{
|
||||
std::set<RTLIL::Cell*> queue;
|
||||
queue.swap(hiercells_queue);
|
||||
|
||||
for (auto cell : queue)
|
||||
{
|
||||
string cell_state = stringf("(|%s_h %s| state)", get_id(module), get_id(cell->name));
|
||||
Module *m = module->design->module(cell->type);
|
||||
log_assert(m != nullptr);
|
||||
|
||||
hier.push_back(stringf(" (= (|%s_is| state) (|%s_is| %s))\n",
|
||||
get_id(module), get_id(cell->type), cell_state.c_str()));
|
||||
|
||||
for (auto &conn : cell->connections())
|
||||
{
|
||||
if (GetSize(conn.second) == 0)
|
||||
continue;
|
||||
|
||||
Wire *w = m->wire(conn.first);
|
||||
SigSpec sig = sigmap(conn.second);
|
||||
|
||||
if (bvmode || GetSize(w) == 1) {
|
||||
hier.push_back(stringf(" (= %s (|%s_n %s| %s)) ; %s.%s\n", (GetSize(w) > 1 ? get_bv(sig) : get_bool(sig)).c_str(),
|
||||
get_id(cell->type), get_id(w), cell_state.c_str(), get_id(cell->type), get_id(w)));
|
||||
} else {
|
||||
for (int i = 0; i < GetSize(w); i++)
|
||||
hier.push_back(stringf(" (= %s (|%s_n %s %d| %s)) ; %s.%s[%d]\n", get_bool(sig[i]).c_str(),
|
||||
get_id(cell->type), get_id(w), i, cell_state.c_str(), get_id(cell->type), get_id(w), i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int iter = 1; !registers.empty(); iter++)
|
||||
|
@ -679,6 +960,7 @@ struct Smt2Worker
|
|||
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(), get_id(cell), log_signal(cell->getPort("\\Q"))));
|
||||
ex_state_eq.push_back(stringf("(= %s %s)", get_bool(cell->getPort("\\Q")).c_str(), get_bool(cell->getPort("\\Q"), "other_state").c_str()));
|
||||
}
|
||||
|
||||
if (cell->type.in("$ff", "$dff"))
|
||||
|
@ -686,13 +968,16 @@ struct Smt2Worker
|
|||
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(), get_id(cell), log_signal(cell->getPort("\\Q"))));
|
||||
ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort("\\Q")).c_str(), get_bv(cell->getPort("\\Q"), "other_state").c_str()));
|
||||
}
|
||||
|
||||
if (cell->type == "$anyconst")
|
||||
if (cell->type.in("$anyconst", "$allconst"))
|
||||
{
|
||||
std::string expr_d = get_bv(cell->getPort("\\Y"));
|
||||
std::string expr_q = get_bv(cell->getPort("\\Y"), "next_state");
|
||||
trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Y"))));
|
||||
if (cell->type == "$anyconst")
|
||||
ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort("\\Y")).c_str(), get_bv(cell->getPort("\\Y"), "other_state").c_str()));
|
||||
}
|
||||
|
||||
if (cell->type == "$mem")
|
||||
|
@ -703,11 +988,93 @@ struct Smt2Worker
|
|||
int width = cell->getParam("\\WIDTH").as_int();
|
||||
int wr_ports = cell->getParam("\\WR_PORTS").as_int();
|
||||
|
||||
bool async_read = false;
|
||||
string initial_memstate, final_memstate;
|
||||
|
||||
if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_ones()) {
|
||||
log_assert(cell->getParam("\\WR_CLK_ENABLE").is_fully_zero());
|
||||
async_read = true;
|
||||
initial_memstate = stringf("%s#%d#0", get_id(module), arrayid);
|
||||
final_memstate = stringf("%s#%d#final", get_id(module), arrayid);
|
||||
}
|
||||
|
||||
if (statebv)
|
||||
{
|
||||
int mem_size = cell->getParam("\\SIZE").as_int();
|
||||
int mem_offset = cell->getParam("\\OFFSET").as_int();
|
||||
|
||||
if (async_read) {
|
||||
makebits(final_memstate, width*mem_size, get_id(cell));
|
||||
}
|
||||
|
||||
for (int i = 0; i < wr_ports; i++)
|
||||
{
|
||||
std::string addr = get_bv(cell->getPort("\\WR_ADDR").extract(abits*i, abits));
|
||||
std::string data = get_bv(cell->getPort("\\WR_DATA").extract(width*i, width));
|
||||
std::string mask = get_bv(cell->getPort("\\WR_EN").extract(width*i, width));
|
||||
SigSpec addr_sig = cell->getPort("\\WR_ADDR").extract(abits*i, abits);
|
||||
SigSpec data_sig = cell->getPort("\\WR_DATA").extract(width*i, width);
|
||||
SigSpec mask_sig = cell->getPort("\\WR_EN").extract(width*i, width);
|
||||
|
||||
std::string addr = get_bv(addr_sig);
|
||||
std::string data = get_bv(data_sig);
|
||||
std::string mask = get_bv(mask_sig);
|
||||
|
||||
decls.push_back(stringf("(define-fun |%s_m:W%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
|
||||
get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
|
||||
addr = stringf("(|%s_m:W%dA %s| state)", get_id(module), i, get_id(cell));
|
||||
|
||||
decls.push_back(stringf("(define-fun |%s_m:W%dD %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
|
||||
get_id(module), i, get_id(cell), get_id(module), width, data.c_str(), log_signal(data_sig)));
|
||||
data = stringf("(|%s_m:W%dD %s| state)", get_id(module), i, get_id(cell));
|
||||
|
||||
decls.push_back(stringf("(define-fun |%s_m:W%dM %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
|
||||
get_id(module), i, get_id(cell), get_id(module), width, mask.c_str(), log_signal(mask_sig)));
|
||||
mask = stringf("(|%s_m:W%dM %s| state)", get_id(module), i, get_id(cell));
|
||||
|
||||
std::string data_expr;
|
||||
|
||||
for (int k = mem_size-1; k >= 0; k--) {
|
||||
std::string new_data = stringf("(bvor (bvand %s %s) (bvand ((_ extract %d %d) (|%s#%d#%d| state)) (bvnot %s)))",
|
||||
data.c_str(), mask.c_str(), width*(k+1)-1, width*k, get_id(module), arrayid, i, mask.c_str());
|
||||
data_expr += stringf("\n (ite (= %s #b%s) %s ((_ extract %d %d) (|%s#%d#%d| state)))",
|
||||
addr.c_str(), Const(k+mem_offset, abits).as_string().c_str(), new_data.c_str(),
|
||||
width*(k+1)-1, width*k, get_id(module), arrayid, i);
|
||||
}
|
||||
|
||||
decls.push_back(stringf("(define-fun |%s#%d#%d| ((state |%s_s|)) (_ BitVec %d) (concat%s)) ; %s\n",
|
||||
get_id(module), arrayid, i+1, get_id(module), width*mem_size, data_expr.c_str(), get_id(cell)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (async_read) {
|
||||
if (statedt)
|
||||
dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
|
||||
initial_memstate.c_str(), abits, width, get_id(cell)));
|
||||
else
|
||||
decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
|
||||
initial_memstate.c_str(), get_id(module), abits, width, get_id(cell)));
|
||||
}
|
||||
|
||||
for (int i = 0; i < wr_ports; i++)
|
||||
{
|
||||
SigSpec addr_sig = cell->getPort("\\WR_ADDR").extract(abits*i, abits);
|
||||
SigSpec data_sig = cell->getPort("\\WR_DATA").extract(width*i, width);
|
||||
SigSpec mask_sig = cell->getPort("\\WR_EN").extract(width*i, width);
|
||||
|
||||
std::string addr = get_bv(addr_sig);
|
||||
std::string data = get_bv(data_sig);
|
||||
std::string mask = get_bv(mask_sig);
|
||||
|
||||
decls.push_back(stringf("(define-fun |%s_m:W%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
|
||||
get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
|
||||
addr = stringf("(|%s_m:W%dA %s| state)", get_id(module), i, get_id(cell));
|
||||
|
||||
decls.push_back(stringf("(define-fun |%s_m:W%dD %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
|
||||
get_id(module), i, get_id(cell), get_id(module), width, data.c_str(), log_signal(data_sig)));
|
||||
data = stringf("(|%s_m:W%dD %s| state)", get_id(module), i, get_id(cell));
|
||||
|
||||
decls.push_back(stringf("(define-fun |%s_m:W%dM %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
|
||||
get_id(module), i, get_id(cell), get_id(module), width, mask.c_str(), log_signal(mask_sig)));
|
||||
mask = stringf("(|%s_m:W%dM %s| state)", get_id(module), i, get_id(cell));
|
||||
|
||||
data = stringf("(bvor (bvand %s %s) (bvand (select (|%s#%d#%d| state) %s) (bvnot %s)))",
|
||||
data.c_str(), mask.c_str(), get_id(module), arrayid, i, addr.c_str(), mask.c_str());
|
||||
|
@ -717,10 +1084,15 @@ struct Smt2Worker
|
|||
get_id(module), arrayid, i+1, get_id(module), abits, width,
|
||||
get_id(module), arrayid, i, addr.c_str(), data.c_str(), get_id(cell)));
|
||||
}
|
||||
}
|
||||
|
||||
std::string expr_d = stringf("(|%s#%d#%d| state)", get_id(module), arrayid, wr_ports);
|
||||
std::string expr_q = stringf("(|%s#%d#0| next_state)", get_id(module), arrayid);
|
||||
trans.push_back(stringf(" (= %s %s) ; %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell)));
|
||||
ex_state_eq.push_back(stringf("(= (|%s#%d#0| state) (|%s#%d#0| other_state))", get_id(module), arrayid, get_id(module), arrayid));
|
||||
|
||||
if (async_read)
|
||||
hier.push_back(stringf(" (= %s (|%s| state)) ; %s\n", expr_d.c_str(), final_memstate.c_str(), get_id(cell)));
|
||||
|
||||
Const init_data = cell->getParam("\\INIT");
|
||||
int memsize = cell->getParam("\\SIZE").as_int();
|
||||
|
@ -737,7 +1109,11 @@ struct Smt2Worker
|
|||
if (bit == State::S0 || bit == State::S1)
|
||||
gen_init_constr = true;
|
||||
|
||||
if (gen_init_constr) {
|
||||
if (gen_init_constr)
|
||||
{
|
||||
if (statebv)
|
||||
/* FIXME */;
|
||||
else
|
||||
init_list.push_back(stringf("(= (select (|%s#%d#0| state) #b%s) #b%s) ; %s[%d]",
|
||||
get_id(module), arrayid, Const(i, abits).as_string().c_str(),
|
||||
initword.as_string().c_str(), get_id(cell), i));
|
||||
|
@ -747,36 +1123,6 @@ struct Smt2Worker
|
|||
}
|
||||
}
|
||||
|
||||
if (verbose) log("=> export logic driving hierarchical cells\n");
|
||||
|
||||
while (!hiercells_queue.empty())
|
||||
{
|
||||
std::set<RTLIL::Cell*> queue;
|
||||
queue.swap(hiercells_queue);
|
||||
|
||||
for (auto cell : queue)
|
||||
{
|
||||
string cell_state = stringf("(|%s_h %s| state)", get_id(module), get_id(cell->name));
|
||||
Module *m = module->design->module(cell->type);
|
||||
log_assert(m != nullptr);
|
||||
|
||||
for (auto &conn : cell->connections())
|
||||
{
|
||||
Wire *w = m->wire(conn.first);
|
||||
SigSpec sig = sigmap(conn.second);
|
||||
|
||||
if (bvmode || GetSize(w) == 1) {
|
||||
hier.push_back(stringf(" (= %s (|%s_n %s| %s)) ; %s.%s\n", (GetSize(w) > 1 ? get_bv(sig) : get_bool(sig)).c_str(),
|
||||
get_id(cell->type), get_id(w), cell_state.c_str(), get_id(cell->type), get_id(w)));
|
||||
} else {
|
||||
for (int i = 0; i < GetSize(w); i++)
|
||||
hier.push_back(stringf(" (= %s (|%s_n %s %d| %s)) ; %s.%s[%d]\n", get_bool(sig[i]).c_str(),
|
||||
get_id(cell->type), get_id(w), i, cell_state.c_str(), get_id(cell->type), get_id(w), i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose) log("=> finalizing SMT2 representation of %s.\n", log_id(module));
|
||||
|
||||
for (auto c : hiercells) {
|
||||
|
@ -786,6 +1132,37 @@ struct Smt2Worker
|
|||
hier.push_back(stringf(" (|%s_h| (|%s_h %s| state))\n", get_id(c->type), get_id(module), get_id(c->name)));
|
||||
trans.push_back(stringf(" (|%s_t| (|%s_h %s| state) (|%s_h %s| next_state))\n",
|
||||
get_id(c->type), get_id(module), get_id(c->name), get_id(module), get_id(c->name)));
|
||||
ex_state_eq.push_back(stringf("(|%s_ex_state_eq| (|%s_h %s| state) (|%s_h %s| other_state))\n",
|
||||
get_id(c->type), get_id(module), get_id(c->name), get_id(module), get_id(c->name)));
|
||||
}
|
||||
|
||||
if (forallmode)
|
||||
{
|
||||
string expr = ex_state_eq.empty() ? "true" : "(and";
|
||||
if (!ex_state_eq.empty()) {
|
||||
if (GetSize(ex_state_eq) == 1) {
|
||||
expr = "\n " + ex_state_eq.front() + "\n";
|
||||
} else {
|
||||
for (auto &str : ex_state_eq)
|
||||
expr += stringf("\n %s", str.c_str());
|
||||
expr += "\n)";
|
||||
}
|
||||
}
|
||||
decls.push_back(stringf("(define-fun |%s_ex_state_eq| ((state |%s_s|) (other_state |%s_s|)) Bool %s)\n",
|
||||
get_id(module), get_id(module), get_id(module), expr.c_str()));
|
||||
|
||||
expr = ex_input_eq.empty() ? "true" : "(and";
|
||||
if (!ex_input_eq.empty()) {
|
||||
if (GetSize(ex_input_eq) == 1) {
|
||||
expr = "\n " + ex_input_eq.front() + "\n";
|
||||
} else {
|
||||
for (auto &str : ex_input_eq)
|
||||
expr += stringf("\n %s", str.c_str());
|
||||
expr += "\n)";
|
||||
}
|
||||
}
|
||||
decls.push_back(stringf("(define-fun |%s_ex_input_eq| ((state |%s_s|) (other_state |%s_s|)) Bool %s)\n",
|
||||
get_id(module), get_id(module), get_id(module), expr.c_str()));
|
||||
}
|
||||
|
||||
string assert_expr = assert_list.empty() ? "true" : "(and";
|
||||
|
@ -832,6 +1209,18 @@ struct Smt2Worker
|
|||
{
|
||||
f << stringf("; yosys-smt2-module %s\n", get_id(module));
|
||||
|
||||
if (statebv) {
|
||||
f << stringf("(define-sort |%s_s| () (_ BitVec %d))\n", get_id(module), statebv_width);
|
||||
mod_stbv_width[module->name] = statebv_width;
|
||||
} else
|
||||
if (statedt) {
|
||||
f << stringf("(declare-datatype |%s_s| ((|%s_mk|\n", get_id(module), get_id(module));
|
||||
for (auto it : dtmembers)
|
||||
f << it;
|
||||
f << stringf(")))\n");
|
||||
} else
|
||||
f << stringf("(declare-sort |%s_s| 0)\n", get_id(module));
|
||||
|
||||
for (auto it : decls)
|
||||
f << it;
|
||||
|
||||
|
@ -864,44 +1253,92 @@ struct Smt2Worker
|
|||
|
||||
struct Smt2Backend : public Backend {
|
||||
Smt2Backend() : Backend("smt2", "write design to SMT-LIBv2 file") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---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("'<mod>' this will declare the sort '<mod>_s' (state of the module) and the\n");
|
||||
log("functions operating on that state.\n");
|
||||
log("'<mod>' this will declare the sort '<mod>_s' (state of the module) and will\n");
|
||||
log("define and declare functions operating on that state.\n");
|
||||
log("\n");
|
||||
log("The '<mod>_s' sort represents a module state. Additional '<mod>_n' functions\n");
|
||||
log("are provided that can be used to access the values of the signals in the module.\n");
|
||||
log("By default only ports, registers, and wires with the 'keep' attribute set are\n");
|
||||
log("made available via such functions. With the -nobv option, multi-bit wires are\n");
|
||||
log("exported as separate functions of type Bool for the individual bits. Without\n");
|
||||
log("-nobv multi-bit wires are exported as single functions of type BitVec.\n");
|
||||
log("The following SMT2 functions are generated for a module with name '<mod>'.\n");
|
||||
log("Some declarations/definitions are printed with a special comment. A prover\n");
|
||||
log("using the SMT2 files can use those comments to collect all relevant metadata\n");
|
||||
log("about the design.\n");
|
||||
log("\n");
|
||||
log("The '<mod>_t' function evaluates to 'true' when the given pair of states\n");
|
||||
log("describes a valid state transition.\n");
|
||||
log(" ; yosys-smt2-module <mod>\n");
|
||||
log(" (declare-sort |<mod>_s| 0)\n");
|
||||
log(" The sort representing a state of module <mod>.\n");
|
||||
log("\n");
|
||||
log("The '<mod>_a' function evaluates to 'true' when the given state satisfies\n");
|
||||
log("the asserts in the module.\n");
|
||||
log(" (define-fun |<mod>_h| ((state |<mod>_s|)) Bool (...))\n");
|
||||
log(" This function must be asserted for each state to establish the\n");
|
||||
log(" design hierarchy.\n");
|
||||
log("\n");
|
||||
log("The '<mod>_u' function evaluates to 'true' when the given state satisfies\n");
|
||||
log("the assumptions in the module.\n");
|
||||
log(" ; yosys-smt2-input <wirename> <width>\n");
|
||||
log(" ; yosys-smt2-output <wirename> <width>\n");
|
||||
log(" ; yosys-smt2-register <wirename> <width>\n");
|
||||
log(" ; yosys-smt2-wire <wirename> <width>\n");
|
||||
log(" (define-fun |<mod>_n <wirename>| (|<mod>_s|) (_ BitVec <width>))\n");
|
||||
log(" (define-fun |<mod>_n <wirename>| (|<mod>_s|) Bool)\n");
|
||||
log(" For each port, register, and wire with the 'keep' attribute set an\n");
|
||||
log(" accessor function is generated. Single-bit wires are returned as Bool,\n");
|
||||
log(" multi-bit wires as BitVec.\n");
|
||||
log("\n");
|
||||
log("The '<mod>_i' function evaluates to 'true' when the given state conforms\n");
|
||||
log("to the initial state. Furthermore the '<mod>_is' function should be asserted\n");
|
||||
log("to be true for initial states in addition to '<mod>_i', and should be\n");
|
||||
log("asserted to be false for non-initial states.\n");
|
||||
log(" ; yosys-smt2-cell <submod> <instancename>\n");
|
||||
log(" (declare-fun |<mod>_h <instancename>| (|<mod>_s|) |<submod>_s|)\n");
|
||||
log(" There is a function like that for each hierarchical instance. It\n");
|
||||
log(" returns the sort that represents the state of the sub-module that\n");
|
||||
log(" implements the instance.\n");
|
||||
log("\n");
|
||||
log("For hierarchical designs, the '<mod>_h' function must be asserted for each\n");
|
||||
log("state to establish the design hierarchy. The '<mod>_h <cellname>' function\n");
|
||||
log("evaluates to the state corresponding to the given cell within <mod>.\n");
|
||||
log(" (declare-fun |<mod>_is| (|<mod>_s|) Bool)\n");
|
||||
log(" This function must be asserted 'true' for initial states, and 'false'\n");
|
||||
log(" otherwise.\n");
|
||||
log("\n");
|
||||
log(" (define-fun |<mod>_i| ((state |<mod>_s|)) Bool (...))\n");
|
||||
log(" This function must be asserted 'true' for initial states. For\n");
|
||||
log(" non-initial states it must be left unconstrained.\n");
|
||||
log("\n");
|
||||
log(" (define-fun |<mod>_t| ((state |<mod>_s|) (next_state |<mod>_s|)) Bool (...))\n");
|
||||
log(" This function evaluates to 'true' if the states 'state' and\n");
|
||||
log(" 'next_state' form a valid state transition.\n");
|
||||
log("\n");
|
||||
log(" (define-fun |<mod>_a| ((state |<mod>_s|)) Bool (...))\n");
|
||||
log(" This function evaluates to 'true' if all assertions hold in the state.\n");
|
||||
log("\n");
|
||||
log(" (define-fun |<mod>_u| ((state |<mod>_s|)) Bool (...))\n");
|
||||
log(" This function evaluates to 'true' if all assumptions hold in the state.\n");
|
||||
log("\n");
|
||||
log(" ; yosys-smt2-assert <id> <filename:linenum>\n");
|
||||
log(" (define-fun |<mod>_a <id>| ((state |<mod>_s|)) Bool (...))\n");
|
||||
log(" Each $assert cell is converted into one of this functions. The function\n");
|
||||
log(" evaluates to 'true' if the assert statement holds in the state.\n");
|
||||
log("\n");
|
||||
log(" ; yosys-smt2-assume <id> <filename:linenum>\n");
|
||||
log(" (define-fun |<mod>_u <id>| ((state |<mod>_s|)) Bool (...))\n");
|
||||
log(" Each $assume cell is converted into one of this functions. The function\n");
|
||||
log(" evaluates to 'true' if the assume statement holds in the state.\n");
|
||||
log("\n");
|
||||
log(" ; yosys-smt2-cover <id> <filename:linenum>\n");
|
||||
log(" (define-fun |<mod>_c <id>| ((state |<mod>_s|)) Bool (...))\n");
|
||||
log(" Each $cover cell is converted into one of this functions. The function\n");
|
||||
log(" evaluates to 'true' if the cover statement is activated in the state.\n");
|
||||
log("\n");
|
||||
log("Options:\n");
|
||||
log("\n");
|
||||
log(" -verbose\n");
|
||||
log(" this will print the recursive walk used to export the modules.\n");
|
||||
log("\n");
|
||||
log(" -stbv\n");
|
||||
log(" Use a BitVec sort to represent a state instead of an uninterpreted\n");
|
||||
log(" sort. As a side-effect this will prevent use of arrays to model\n");
|
||||
log(" memories.\n");
|
||||
log("\n");
|
||||
log(" -stdt\n");
|
||||
log(" Use SMT-LIB 2.6 style datatypes to represent a state instead of an\n");
|
||||
log(" uninterpreted sort.\n");
|
||||
log("\n");
|
||||
log(" -nobv\n");
|
||||
log(" disable support for BitVec (FixedSizeBitVectors theory). without this\n");
|
||||
log(" option multi-bit wires are represented using the BitVec sort and\n");
|
||||
|
@ -972,10 +1409,11 @@ struct Smt2Backend : public Backend {
|
|||
log("from non-zero to zero in the test design.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::ifstream template_f;
|
||||
bool bvmode = true, memmode = true, wiresmode = false, verbose = false;
|
||||
bool bvmode = true, memmode = true, wiresmode = false, verbose = false, statebv = false, statedt = false;
|
||||
bool forallmode = false;
|
||||
|
||||
log_header(design, "Executing SMT2 backend.\n");
|
||||
|
||||
|
@ -992,6 +1430,16 @@ struct Smt2Backend : public Backend {
|
|||
log_warning("Options -bv and -mem are now the default. Support for -bv and -mem will be removed in the future.\n");
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-stbv") {
|
||||
statebv = true;
|
||||
statedt = false;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-stdt") {
|
||||
statebv = false;
|
||||
statedt = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nobv") {
|
||||
bvmode = false;
|
||||
memmode = false;
|
||||
|
@ -1033,6 +1481,12 @@ struct Smt2Backend : public Backend {
|
|||
if (!memmode)
|
||||
*f << stringf("; yosys-smt2-nomem\n");
|
||||
|
||||
if (statebv)
|
||||
*f << stringf("; yosys-smt2-stbv\n");
|
||||
|
||||
if (statedt)
|
||||
*f << stringf("; yosys-smt2-stdt\n");
|
||||
|
||||
std::vector<RTLIL::Module*> sorted_modules;
|
||||
|
||||
// extract module dependencies
|
||||
|
@ -1062,9 +1516,23 @@ struct Smt2Backend : public Backend {
|
|||
module_deps.erase(sorted_modules.at(sorted_modules_idx++));
|
||||
}
|
||||
|
||||
dict<IdString, int> mod_stbv_width;
|
||||
dict<IdString, dict<IdString, pair<bool, bool>>> mod_clk_cache;
|
||||
Module *topmod = design->top_module();
|
||||
std::string topmod_id;
|
||||
|
||||
for (auto module : sorted_modules)
|
||||
for (auto cell : module->cells())
|
||||
if (cell->type.in("$allconst", "$allseq"))
|
||||
goto found_forall;
|
||||
if (0) {
|
||||
found_forall:
|
||||
forallmode = true;
|
||||
*f << stringf("; yosys-smt2-forall\n");
|
||||
if (!statebv && !statedt)
|
||||
log_error("Forall-exists problems are only supported in -stbv or -stdt mode.\n");
|
||||
}
|
||||
|
||||
for (auto module : sorted_modules)
|
||||
{
|
||||
if (module->get_bool_attribute("\\blackbox") || module->has_memories_warn() || module->has_processes_warn())
|
||||
|
@ -1072,7 +1540,7 @@ struct Smt2Backend : public Backend {
|
|||
|
||||
log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module));
|
||||
|
||||
Smt2Worker worker(module, bvmode, memmode, wiresmode, verbose);
|
||||
Smt2Worker worker(module, bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode, mod_stbv_width, mod_clk_cache);
|
||||
worker.run();
|
||||
worker.write(*f);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,3 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# yosys -- Yosys Open SYnthesis Suite
|
||||
#
|
||||
|
@ -17,10 +16,63 @@
|
|||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
import sys, subprocess, re
|
||||
import sys, re, os, signal
|
||||
import subprocess
|
||||
if os.name == "posix":
|
||||
import resource
|
||||
from copy import deepcopy
|
||||
from select import select
|
||||
from time import time
|
||||
from queue import Queue, Empty
|
||||
from threading import Thread
|
||||
|
||||
|
||||
# This is needed so that the recursive SMT2 S-expression parser
|
||||
# does not run out of stack frames when parsing large expressions
|
||||
if os.name == "posix":
|
||||
smtio_reclimit = 64 * 1024
|
||||
if sys.getrecursionlimit() < smtio_reclimit:
|
||||
sys.setrecursionlimit(smtio_reclimit)
|
||||
|
||||
current_rlimit_stack = resource.getrlimit(resource.RLIMIT_STACK)
|
||||
if current_rlimit_stack[0] != resource.RLIM_INFINITY:
|
||||
smtio_stacksize = 128 * 1024 * 1024
|
||||
if os.uname().sysname == "Darwin":
|
||||
# MacOS has rather conservative stack limits
|
||||
smtio_stacksize = 16 * 1024 * 1024
|
||||
if current_rlimit_stack[1] != resource.RLIM_INFINITY:
|
||||
smtio_stacksize = min(smtio_stacksize, current_rlimit_stack[1])
|
||||
if current_rlimit_stack[0] < smtio_stacksize:
|
||||
resource.setrlimit(resource.RLIMIT_STACK, (smtio_stacksize, current_rlimit_stack[1]))
|
||||
|
||||
|
||||
# currently running solvers (so we can kill them)
|
||||
running_solvers = dict()
|
||||
forced_shutdown = False
|
||||
solvers_index = 0
|
||||
|
||||
def force_shutdown(signum, frame):
|
||||
global forced_shutdown
|
||||
if not forced_shutdown:
|
||||
forced_shutdown = True
|
||||
if signum is not None:
|
||||
print("<%s>" % signal.Signals(signum).name)
|
||||
for p in running_solvers.values():
|
||||
# os.killpg(os.getpgid(p.pid), signal.SIGTERM)
|
||||
os.kill(p.pid, signal.SIGTERM)
|
||||
sys.exit(1)
|
||||
|
||||
if os.name == "posix":
|
||||
signal.signal(signal.SIGHUP, force_shutdown)
|
||||
signal.signal(signal.SIGINT, force_shutdown)
|
||||
signal.signal(signal.SIGTERM, force_shutdown)
|
||||
|
||||
def except_hook(exctype, value, traceback):
|
||||
if not forced_shutdown:
|
||||
sys.__excepthook__(exctype, value, traceback)
|
||||
force_shutdown(None, None)
|
||||
|
||||
sys.excepthook = except_hook
|
||||
|
||||
|
||||
hex_dict = {
|
||||
|
@ -41,25 +93,38 @@ class SmtModInfo:
|
|||
self.memories = dict()
|
||||
self.wires = set()
|
||||
self.wsize = dict()
|
||||
self.clocks = dict()
|
||||
self.cells = dict()
|
||||
self.asserts = dict()
|
||||
self.covers = dict()
|
||||
self.anyconsts = dict()
|
||||
self.anyseqs = dict()
|
||||
self.allconsts = dict()
|
||||
self.allseqs = dict()
|
||||
self.asize = dict()
|
||||
|
||||
|
||||
class SmtIo:
|
||||
def __init__(self, opts=None):
|
||||
global solvers_index
|
||||
|
||||
self.logic = None
|
||||
self.logic_qf = True
|
||||
self.logic_ax = True
|
||||
self.logic_uf = True
|
||||
self.logic_bv = True
|
||||
self.logic_dt = False
|
||||
self.forall = False
|
||||
self.produce_models = True
|
||||
self.smt2cache = [list()]
|
||||
self.p = None
|
||||
self.p_index = solvers_index
|
||||
solvers_index += 1
|
||||
|
||||
if opts is not None:
|
||||
self.logic = opts.logic
|
||||
self.solver = opts.solver
|
||||
self.solver_opts = opts.solver_opts
|
||||
self.debug_print = opts.debug_print
|
||||
self.debug_file = opts.debug_file
|
||||
self.dummy_file = opts.dummy_file
|
||||
|
@ -70,33 +135,65 @@ class SmtIo:
|
|||
self.nocomments = opts.nocomments
|
||||
|
||||
else:
|
||||
self.solver = "z3"
|
||||
self.solver = "yices"
|
||||
self.solver_opts = list()
|
||||
self.debug_print = False
|
||||
self.debug_file = None
|
||||
self.dummy_file = None
|
||||
self.timeinfo = True
|
||||
self.timeinfo = os.name != "nt"
|
||||
self.unroll = False
|
||||
self.noincr = False
|
||||
self.info_stmts = list()
|
||||
self.nocomments = False
|
||||
|
||||
self.start_time = time()
|
||||
|
||||
self.modinfo = dict()
|
||||
self.curmod = None
|
||||
self.topmod = None
|
||||
self.setup_done = False
|
||||
|
||||
def __del__(self):
|
||||
if self.p is not None and not forced_shutdown:
|
||||
os.killpg(os.getpgid(self.p.pid), signal.SIGTERM)
|
||||
if running_solvers is not None:
|
||||
del running_solvers[self.p_index]
|
||||
|
||||
def setup(self):
|
||||
assert not self.setup_done
|
||||
|
||||
if self.forall:
|
||||
self.unroll = False
|
||||
|
||||
if self.solver == "yices":
|
||||
self.popen_vargs = ['yices-smt2', '--incremental']
|
||||
if self.noincr:
|
||||
self.popen_vargs = ['yices-smt2'] + self.solver_opts
|
||||
else:
|
||||
self.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts
|
||||
|
||||
if self.solver == "z3":
|
||||
self.popen_vargs = ['z3', '-smt2', '-in']
|
||||
self.popen_vargs = ['z3', '-smt2', '-in'] + self.solver_opts
|
||||
|
||||
if self.solver == "cvc4":
|
||||
self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2']
|
||||
if self.noincr:
|
||||
self.popen_vargs = ['cvc4', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts
|
||||
else:
|
||||
self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts
|
||||
|
||||
if self.solver == "mathsat":
|
||||
self.popen_vargs = ['mathsat']
|
||||
self.popen_vargs = ['mathsat'] + self.solver_opts
|
||||
|
||||
if self.solver == "boolector":
|
||||
self.popen_vargs = ['boolector', '--smt2', '-i']
|
||||
if self.noincr:
|
||||
self.popen_vargs = ['boolector', '--smt2'] + self.solver_opts
|
||||
else:
|
||||
self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts
|
||||
self.unroll = True
|
||||
|
||||
if self.solver == "abc":
|
||||
if len(self.solver_opts) > 0:
|
||||
self.popen_vargs = ['yosys-abc', '-S', '; '.join(self.solver_opts)]
|
||||
else:
|
||||
self.popen_vargs = ['yosys-abc', '-S', '%blast; &sweep -C 5000; &syn4; &cec -s -m -C 2000']
|
||||
self.logic_ax = False
|
||||
self.unroll = True
|
||||
|
@ -109,9 +206,10 @@ class SmtIo:
|
|||
if self.dummy_file is not None:
|
||||
self.dummy_fd = open(self.dummy_file, "w")
|
||||
if not self.noincr:
|
||||
self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
self.p_open()
|
||||
|
||||
if self.unroll:
|
||||
assert not self.forall
|
||||
self.logic_uf = False
|
||||
self.unroll_idcnt = 0
|
||||
self.unroll_buffer = ""
|
||||
|
@ -121,36 +219,27 @@ class SmtIo:
|
|||
self.unroll_cache = dict()
|
||||
self.unroll_stack = list()
|
||||
|
||||
self.start_time = time()
|
||||
|
||||
self.modinfo = dict()
|
||||
self.curmod = None
|
||||
self.topmod = None
|
||||
self.setup_done = False
|
||||
|
||||
def setup(self):
|
||||
assert not self.setup_done
|
||||
|
||||
if self.logic is None:
|
||||
self.logic = ""
|
||||
if self.logic_qf: self.logic += "QF_"
|
||||
if self.logic_ax: self.logic += "A"
|
||||
if self.logic_uf: self.logic += "UF"
|
||||
if self.logic_bv: self.logic += "BV"
|
||||
if self.logic_dt: self.logic = "ALL"
|
||||
|
||||
self.setup_done = True
|
||||
|
||||
for stmt in self.info_stmts:
|
||||
self.write(stmt)
|
||||
|
||||
if self.produce_models:
|
||||
self.write("(set-option :produce-models true)")
|
||||
|
||||
self.write("(set-logic %s)" % self.logic)
|
||||
|
||||
for stmt in self.info_stmts:
|
||||
self.write(stmt)
|
||||
|
||||
def timestamp(self):
|
||||
secs = int(time() - self.start_time)
|
||||
return "## %6d %3d:%02d:%02d " % (secs, secs // (60*60), (secs // 60) % 60, secs % 60)
|
||||
return "## %3d:%02d:%02d " % (secs // (60*60), (secs // 60) % 60, secs % 60)
|
||||
|
||||
def replace_in_stmt(self, stmt, pat, repl):
|
||||
if stmt == pat:
|
||||
|
@ -201,18 +290,75 @@ class SmtIo:
|
|||
|
||||
return stmt
|
||||
|
||||
def p_thread_main(self):
|
||||
while True:
|
||||
data = self.p.stdout.readline().decode("ascii")
|
||||
if data == "": break
|
||||
self.p_queue.put(data)
|
||||
self.p_queue.put("")
|
||||
self.p_running = False
|
||||
|
||||
def p_open(self):
|
||||
assert self.p is None
|
||||
self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
running_solvers[self.p_index] = self.p
|
||||
self.p_running = True
|
||||
self.p_next = None
|
||||
self.p_queue = Queue()
|
||||
self.p_thread = Thread(target=self.p_thread_main)
|
||||
self.p_thread.start()
|
||||
|
||||
def p_write(self, data, flush):
|
||||
assert self.p is not None
|
||||
self.p.stdin.write(bytes(data, "ascii"))
|
||||
if flush: self.p.stdin.flush()
|
||||
|
||||
def p_read(self):
|
||||
assert self.p is not None
|
||||
if self.p_next is not None:
|
||||
data = self.p_next
|
||||
self.p_next = None
|
||||
return data
|
||||
if not self.p_running:
|
||||
return ""
|
||||
return self.p_queue.get()
|
||||
|
||||
def p_poll(self, timeout=0.1):
|
||||
assert self.p is not None
|
||||
assert self.p_running
|
||||
if self.p_next is not None:
|
||||
return False
|
||||
try:
|
||||
self.p_next = self.p_queue.get(True, timeout)
|
||||
return False
|
||||
except Empty:
|
||||
return True
|
||||
|
||||
def p_close(self):
|
||||
assert self.p is not None
|
||||
self.p.stdin.close()
|
||||
self.p_thread.join()
|
||||
assert not self.p_running
|
||||
del running_solvers[self.p_index]
|
||||
self.p = None
|
||||
self.p_next = None
|
||||
self.p_queue = None
|
||||
self.p_thread = None
|
||||
|
||||
def write(self, stmt, unroll=True):
|
||||
if stmt.startswith(";"):
|
||||
self.info(stmt)
|
||||
if not self.setup_done:
|
||||
self.info_stmts.append(stmt)
|
||||
return
|
||||
elif not self.setup_done:
|
||||
self.setup()
|
||||
|
||||
stmt = stmt.strip()
|
||||
|
||||
if self.nocomments or self.unroll:
|
||||
if stmt.startswith(";"):
|
||||
return
|
||||
stmt = re.sub(r" ;.*", "", stmt)
|
||||
stmt = re.sub(r" *;.*", "", stmt)
|
||||
if stmt == "": return
|
||||
|
||||
if unroll and self.unroll:
|
||||
stmt = self.unroll_buffer + stmt
|
||||
|
@ -271,20 +417,17 @@ class SmtIo:
|
|||
if self.solver != "dummy":
|
||||
if self.noincr:
|
||||
if self.p is not None and not stmt.startswith("(get-"):
|
||||
self.p.stdin.close()
|
||||
self.p = None
|
||||
self.p_close()
|
||||
if stmt == "(push 1)":
|
||||
self.smt2cache.append(list())
|
||||
elif stmt == "(pop 1)":
|
||||
self.smt2cache.pop()
|
||||
else:
|
||||
if self.p is not None:
|
||||
self.p.stdin.write(bytes(stmt + "\n", "ascii"))
|
||||
self.p.stdin.flush()
|
||||
self.p_write(stmt + "\n", True)
|
||||
self.smt2cache[-1].append(stmt)
|
||||
else:
|
||||
self.p.stdin.write(bytes(stmt + "\n", "ascii"))
|
||||
self.p.stdin.flush()
|
||||
self.p_write(stmt + "\n", True)
|
||||
|
||||
def info(self, stmt):
|
||||
if not stmt.startswith("; yosys-smt2-"):
|
||||
|
@ -300,6 +443,15 @@ class SmtIo:
|
|||
if self.logic is None:
|
||||
self.logic_bv = False
|
||||
|
||||
if fields[1] == "yosys-smt2-stdt":
|
||||
if self.logic is None:
|
||||
self.logic_dt = True
|
||||
|
||||
if fields[1] == "yosys-smt2-forall":
|
||||
if self.logic is None:
|
||||
self.logic_qf = False
|
||||
self.forall = True
|
||||
|
||||
if fields[1] == "yosys-smt2-module":
|
||||
self.curmod = fields[2]
|
||||
self.modinfo[self.curmod] = SmtModInfo()
|
||||
|
@ -323,17 +475,40 @@ class SmtIo:
|
|||
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
|
||||
|
||||
if fields[1] == "yosys-smt2-memory":
|
||||
self.modinfo[self.curmod].memories[fields[2]] = (int(fields[3]), int(fields[4]), int(fields[5]))
|
||||
self.modinfo[self.curmod].memories[fields[2]] = (int(fields[3]), int(fields[4]), int(fields[5]), int(fields[6]), fields[7] == "async")
|
||||
|
||||
if fields[1] == "yosys-smt2-wire":
|
||||
self.modinfo[self.curmod].wires.add(fields[2])
|
||||
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
|
||||
|
||||
if fields[1] == "yosys-smt2-clock":
|
||||
for edge in fields[3:]:
|
||||
if fields[2] not in self.modinfo[self.curmod].clocks:
|
||||
self.modinfo[self.curmod].clocks[fields[2]] = edge
|
||||
elif self.modinfo[self.curmod].clocks[fields[2]] != edge:
|
||||
self.modinfo[self.curmod].clocks[fields[2]] = "event"
|
||||
|
||||
if fields[1] == "yosys-smt2-assert":
|
||||
self.modinfo[self.curmod].asserts[fields[2]] = fields[3]
|
||||
self.modinfo[self.curmod].asserts["%s_a %s" % (self.curmod, fields[2])] = fields[3]
|
||||
|
||||
if fields[1] == "yosys-smt2-cover":
|
||||
self.modinfo[self.curmod].covers["%s_c %s" % (self.curmod, fields[2])] = fields[3]
|
||||
|
||||
if fields[1] == "yosys-smt2-anyconst":
|
||||
self.modinfo[self.curmod].anyconsts[fields[2]] = fields[3]
|
||||
self.modinfo[self.curmod].anyconsts[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5])
|
||||
self.modinfo[self.curmod].asize[fields[2]] = int(fields[3])
|
||||
|
||||
if fields[1] == "yosys-smt2-anyseq":
|
||||
self.modinfo[self.curmod].anyseqs[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5])
|
||||
self.modinfo[self.curmod].asize[fields[2]] = int(fields[3])
|
||||
|
||||
if fields[1] == "yosys-smt2-allconst":
|
||||
self.modinfo[self.curmod].allconsts[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5])
|
||||
self.modinfo[self.curmod].asize[fields[2]] = int(fields[3])
|
||||
|
||||
if fields[1] == "yosys-smt2-allseq":
|
||||
self.modinfo[self.curmod].allseqs[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5])
|
||||
self.modinfo[self.curmod].asize[fields[2]] = int(fields[3])
|
||||
|
||||
def hiernets(self, top, regs_only=False):
|
||||
def hiernets_worker(nets, mod, cursor):
|
||||
|
@ -347,6 +522,54 @@ class SmtIo:
|
|||
hiernets_worker(nets, top, [])
|
||||
return nets
|
||||
|
||||
def hieranyconsts(self, top):
|
||||
def worker(results, mod, cursor):
|
||||
for name, value in sorted(self.modinfo[mod].anyconsts.items()):
|
||||
width = self.modinfo[mod].asize[name]
|
||||
results.append((cursor, name, value[0], value[1], width))
|
||||
for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
|
||||
worker(results, celltype, cursor + [cellname])
|
||||
|
||||
results = list()
|
||||
worker(results, top, [])
|
||||
return results
|
||||
|
||||
def hieranyseqs(self, top):
|
||||
def worker(results, mod, cursor):
|
||||
for name, value in sorted(self.modinfo[mod].anyseqs.items()):
|
||||
width = self.modinfo[mod].asize[name]
|
||||
results.append((cursor, name, value[0], value[1], width))
|
||||
for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
|
||||
worker(results, celltype, cursor + [cellname])
|
||||
|
||||
results = list()
|
||||
worker(results, top, [])
|
||||
return results
|
||||
|
||||
def hierallconsts(self, top):
|
||||
def worker(results, mod, cursor):
|
||||
for name, value in sorted(self.modinfo[mod].allconsts.items()):
|
||||
width = self.modinfo[mod].asize[name]
|
||||
results.append((cursor, name, value[0], value[1], width))
|
||||
for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
|
||||
worker(results, celltype, cursor + [cellname])
|
||||
|
||||
results = list()
|
||||
worker(results, top, [])
|
||||
return results
|
||||
|
||||
def hierallseqs(self, top):
|
||||
def worker(results, mod, cursor):
|
||||
for name, value in sorted(self.modinfo[mod].allseqs.items()):
|
||||
width = self.modinfo[mod].asize[name]
|
||||
results.append((cursor, name, value[0], value[1], width))
|
||||
for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
|
||||
worker(results, celltype, cursor + [cellname])
|
||||
|
||||
results = list()
|
||||
worker(results, top, [])
|
||||
return results
|
||||
|
||||
def hiermems(self, top):
|
||||
def hiermems_worker(mems, mod, cursor):
|
||||
for memname in sorted(self.modinfo[mod].memories.keys()):
|
||||
|
@ -366,7 +589,7 @@ class SmtIo:
|
|||
if self.solver == "dummy":
|
||||
line = self.dummy_fd.readline().strip()
|
||||
else:
|
||||
line = self.p.stdout.readline().decode("ascii").strip()
|
||||
line = self.p_read().strip()
|
||||
if self.dummy_file is not None:
|
||||
self.dummy_fd.write(line + "\n")
|
||||
|
||||
|
@ -379,12 +602,14 @@ class SmtIo:
|
|||
if count_brackets == 0:
|
||||
break
|
||||
if self.solver != "dummy" and self.p.poll():
|
||||
print("SMT Solver terminated unexpectedly: %s" % "".join(stmt))
|
||||
print("%s Solver terminated unexpectedly: %s" % (self.timestamp(), "".join(stmt)), flush=True)
|
||||
sys.exit(1)
|
||||
|
||||
stmt = "".join(stmt)
|
||||
if stmt.startswith("(error"):
|
||||
print("SMT Solver Error: %s" % stmt, file=sys.stderr)
|
||||
print("%s Solver Error: %s" % (self.timestamp(), stmt), flush=True)
|
||||
if self.solver != "dummy":
|
||||
self.p_close()
|
||||
sys.exit(1)
|
||||
|
||||
return stmt
|
||||
|
@ -399,15 +624,13 @@ class SmtIo:
|
|||
if self.solver != "dummy":
|
||||
if self.noincr:
|
||||
if self.p is not None:
|
||||
self.p.stdin.close()
|
||||
self.p = None
|
||||
self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
self.p_close()
|
||||
self.p_open()
|
||||
for cache_ctx in self.smt2cache:
|
||||
for cache_stmt in cache_ctx:
|
||||
self.p.stdin.write(bytes(cache_stmt + "\n", "ascii"))
|
||||
self.p_write(cache_stmt + "\n", False)
|
||||
|
||||
self.p.stdin.write(bytes("(check-sat)\n", "ascii"))
|
||||
self.p.stdin.flush()
|
||||
self.p_write("(check-sat)\n", True)
|
||||
|
||||
if self.timeinfo:
|
||||
i = 0
|
||||
|
@ -415,7 +638,7 @@ class SmtIo:
|
|||
|
||||
count = 0
|
||||
num_bs = 0
|
||||
while select([self.p.stdout], [], [], 0.1) == ([], [], []):
|
||||
while self.p_poll():
|
||||
count += 1
|
||||
|
||||
if count < 25:
|
||||
|
@ -444,11 +667,43 @@ class SmtIo:
|
|||
print("\b \b" * num_bs, end="", file=sys.stderr)
|
||||
sys.stderr.flush()
|
||||
|
||||
else:
|
||||
count = 0
|
||||
while self.p_poll(60):
|
||||
count += 1
|
||||
msg = None
|
||||
|
||||
if count == 1:
|
||||
msg = "1 minute"
|
||||
|
||||
elif count in [5, 10, 15, 30]:
|
||||
msg = "%d minutes" % count
|
||||
|
||||
elif count == 60:
|
||||
msg = "1 hour"
|
||||
|
||||
elif count % 60 == 0:
|
||||
msg = "%d hours" % (count // 60)
|
||||
|
||||
if msg is not None:
|
||||
print("%s waiting for solver (%s)" % (self.timestamp(), msg), flush=True)
|
||||
|
||||
result = self.read()
|
||||
|
||||
if self.debug_file:
|
||||
print("(set-info :status %s)" % result, file=self.debug_file)
|
||||
print("(check-sat)", file=self.debug_file)
|
||||
self.debug_file.flush()
|
||||
|
||||
if result not in ["sat", "unsat"]:
|
||||
if result == "":
|
||||
print("%s Unexpected EOF response from solver." % (self.timestamp()), flush=True)
|
||||
else:
|
||||
print("%s Unexpected response from solver: %s" % (self.timestamp(), result), flush=True)
|
||||
if self.solver != "dummy":
|
||||
self.p_close()
|
||||
sys.exit(1)
|
||||
|
||||
return result
|
||||
|
||||
def parse(self, stmt):
|
||||
|
@ -503,6 +758,9 @@ class SmtIo:
|
|||
return h
|
||||
|
||||
def bv2bin(self, v):
|
||||
if type(v) is list and len(v) == 3 and v[0] == "_" and v[1].startswith("bv"):
|
||||
x, n = int(v[1][2:]), int(v[2])
|
||||
return "".join("1" if (x & (1 << i)) else "0" for i in range(n-1, -1, -1))
|
||||
if v == "true": return "1"
|
||||
if v == "false": return "0"
|
||||
if v.startswith("#b"):
|
||||
|
@ -539,6 +797,9 @@ class SmtIo:
|
|||
return [".".join(path)]
|
||||
|
||||
def net_expr(self, mod, base, path):
|
||||
if len(path) == 0:
|
||||
return base
|
||||
|
||||
if len(path) == 1:
|
||||
assert mod in self.modinfo
|
||||
if path[0] == "":
|
||||
|
@ -568,23 +829,54 @@ class SmtIo:
|
|||
assert net_path[-1] in self.modinfo[mod].wsize
|
||||
return self.modinfo[mod].wsize[net_path[-1]]
|
||||
|
||||
def mem_expr(self, mod, base, path, portidx=None, infomode=False):
|
||||
def net_clock(self, mod, net_path):
|
||||
for i in range(len(net_path)-1):
|
||||
assert mod in self.modinfo
|
||||
assert net_path[i] in self.modinfo[mod].cells
|
||||
mod = self.modinfo[mod].cells[net_path[i]]
|
||||
|
||||
assert mod in self.modinfo
|
||||
if net_path[-1] not in self.modinfo[mod].clocks:
|
||||
return None
|
||||
return self.modinfo[mod].clocks[net_path[-1]]
|
||||
|
||||
def net_exists(self, mod, net_path):
|
||||
for i in range(len(net_path)-1):
|
||||
if mod not in self.modinfo: return False
|
||||
if net_path[i] not in self.modinfo[mod].cells: return False
|
||||
mod = self.modinfo[mod].cells[net_path[i]]
|
||||
|
||||
if mod not in self.modinfo: return False
|
||||
if net_path[-1] not in self.modinfo[mod].wsize: return False
|
||||
return True
|
||||
|
||||
def mem_exists(self, mod, mem_path):
|
||||
for i in range(len(mem_path)-1):
|
||||
if mod not in self.modinfo: return False
|
||||
if mem_path[i] not in self.modinfo[mod].cells: return False
|
||||
mod = self.modinfo[mod].cells[mem_path[i]]
|
||||
|
||||
if mod not in self.modinfo: return False
|
||||
if mem_path[-1] not in self.modinfo[mod].memories: return False
|
||||
return True
|
||||
|
||||
def mem_expr(self, mod, base, path, port=None, infomode=False):
|
||||
if len(path) == 1:
|
||||
assert mod in self.modinfo
|
||||
assert path[0] in self.modinfo[mod].memories
|
||||
if infomode:
|
||||
return self.modinfo[mod].memories[path[0]]
|
||||
return "(|%s_m%s %s| %s)" % (mod, "" if portidx is None else ":%d" % portidx, path[0], base)
|
||||
return "(|%s_m%s %s| %s)" % (mod, "" if port is None else ":%s" % port, path[0], base)
|
||||
|
||||
assert mod in self.modinfo
|
||||
assert path[0] in self.modinfo[mod].cells
|
||||
|
||||
nextmod = self.modinfo[mod].cells[path[0]]
|
||||
nextbase = "(|%s_h %s| %s)" % (mod, path[0], base)
|
||||
return self.mem_expr(nextmod, nextbase, path[1:], portidx=portidx, infomode=infomode)
|
||||
return self.mem_expr(nextmod, nextbase, path[1:], port=port, infomode=infomode)
|
||||
|
||||
def mem_info(self, mod, base, path):
|
||||
return self.mem_expr(mod, base, path, infomode=True)
|
||||
def mem_info(self, mod, path):
|
||||
return self.mem_expr(mod, "", path, infomode=True)
|
||||
|
||||
def get_net(self, mod_name, net_path, state_name):
|
||||
return self.get(self.net_expr(mod_name, state_name, net_path))
|
||||
|
@ -607,19 +899,21 @@ class SmtIo:
|
|||
def wait(self):
|
||||
if self.p is not None:
|
||||
self.p.wait()
|
||||
self.p_close()
|
||||
|
||||
|
||||
class SmtOpts:
|
||||
def __init__(self):
|
||||
self.shortopts = "s:v"
|
||||
self.shortopts = "s:S:v"
|
||||
self.longopts = ["unroll", "noincr", "noprogress", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"]
|
||||
self.solver = "z3"
|
||||
self.solver = "yices"
|
||||
self.solver_opts = list()
|
||||
self.debug_print = False
|
||||
self.debug_file = None
|
||||
self.dummy_file = None
|
||||
self.unroll = False
|
||||
self.noincr = False
|
||||
self.timeinfo = True
|
||||
self.timeinfo = os.name != "nt"
|
||||
self.logic = None
|
||||
self.info_stmts = list()
|
||||
self.nocomments = False
|
||||
|
@ -627,6 +921,8 @@ class SmtOpts:
|
|||
def handle(self, o, a):
|
||||
if o == "-s":
|
||||
self.solver = a
|
||||
elif o == "-S":
|
||||
self.solver_opts.append(a)
|
||||
elif o == "-v":
|
||||
self.debug_print = True
|
||||
elif o == "--unroll":
|
||||
|
@ -634,7 +930,7 @@ class SmtOpts:
|
|||
elif o == "--noincr":
|
||||
self.noincr = True
|
||||
elif o == "--noprogress":
|
||||
self.timeinfo = True
|
||||
self.timeinfo = False
|
||||
elif o == "--dump-smt2":
|
||||
self.debug_file = open(a, "w")
|
||||
elif o == "--logic":
|
||||
|
@ -652,8 +948,11 @@ class SmtOpts:
|
|||
def helpmsg(self):
|
||||
return """
|
||||
-s <solver>
|
||||
set SMT solver: z3, cvc4, yices, mathsat, boolector, dummy
|
||||
default: z3
|
||||
set SMT solver: z3, yices, boolector, cvc4, mathsat, dummy
|
||||
default: yices
|
||||
|
||||
-S <opt>
|
||||
pass <opt> as command line argument to the solver
|
||||
|
||||
--logic <smt2_logic>
|
||||
use the specified SMT2 logic (e.g. QF_AUFBV)
|
||||
|
@ -674,6 +973,7 @@ class SmtOpts:
|
|||
|
||||
--noprogress
|
||||
disable timer display during solving
|
||||
(this option is set implicitly on Windows)
|
||||
|
||||
--dump-smt2 <filename>
|
||||
write smt2 statements to file
|
||||
|
@ -691,6 +991,7 @@ class MkVcd:
|
|||
self.f = f
|
||||
self.t = -1
|
||||
self.nets = dict()
|
||||
self.clocks = dict()
|
||||
|
||||
def add_net(self, path, width):
|
||||
path = tuple(path)
|
||||
|
@ -698,34 +999,78 @@ class MkVcd:
|
|||
key = "n%d" % len(self.nets)
|
||||
self.nets[path] = (key, width)
|
||||
|
||||
def add_clock(self, path, edge):
|
||||
path = tuple(path)
|
||||
assert self.t == -1
|
||||
key = "n%d" % len(self.nets)
|
||||
self.nets[path] = (key, 1)
|
||||
self.clocks[path] = (key, edge)
|
||||
|
||||
def set_net(self, path, bits):
|
||||
path = tuple(path)
|
||||
assert self.t >= 0
|
||||
assert path in self.nets
|
||||
if path not in self.clocks:
|
||||
print("b%s %s" % (bits, self.nets[path][0]), file=self.f)
|
||||
|
||||
def escape_name(self, name):
|
||||
name = re.sub(r"\[([0-9a-zA-Z_]*[a-zA-Z_][0-9a-zA-Z_]*)\]", r"<\1>", name)
|
||||
if re.match("[\[\]]", name) and name[0] != "\\":
|
||||
name = "\\" + name
|
||||
return name
|
||||
|
||||
def set_time(self, t):
|
||||
assert t >= self.t
|
||||
if t != self.t:
|
||||
if self.t == -1:
|
||||
print("$var integer 32 t smt_step $end", file=self.f)
|
||||
print("$var event 1 ! smt_clock $end", file=self.f)
|
||||
|
||||
scope = []
|
||||
for path in sorted(self.nets):
|
||||
while len(scope)+1 > len(path) or (len(scope) > 0 and scope[-1] != path[len(scope)-1]):
|
||||
key, width = self.nets[path]
|
||||
|
||||
uipath = list(path)
|
||||
if "." in uipath[-1]:
|
||||
uipath = uipath[0:-1] + uipath[-1].split(".")
|
||||
for i in range(len(uipath)):
|
||||
uipath[i] = re.sub(r"\[([^\]]*)\]", r"<\1>", uipath[i])
|
||||
|
||||
while uipath[:len(scope)] != scope:
|
||||
print("$upscope $end", file=self.f)
|
||||
scope = scope[:-1]
|
||||
while len(scope)+1 < len(path):
|
||||
print("$scope module %s $end" % path[len(scope)], file=self.f)
|
||||
scope.append(path[len(scope)-1])
|
||||
key, width = self.nets[path]
|
||||
print("$var wire %d %s %s $end" % (width, key, path[-1]), file=self.f)
|
||||
|
||||
while uipath[:-1] != scope:
|
||||
print("$scope module %s $end" % uipath[len(scope)], file=self.f)
|
||||
scope.append(uipath[len(scope)])
|
||||
|
||||
if path in self.clocks and self.clocks[path][1] == "event":
|
||||
print("$var event 1 %s %s $end" % (key, uipath[-1]), file=self.f)
|
||||
else:
|
||||
print("$var wire %d %s %s $end" % (width, key, uipath[-1]), file=self.f)
|
||||
|
||||
for i in range(len(scope)):
|
||||
print("$upscope $end", file=self.f)
|
||||
|
||||
print("$enddefinitions $end", file=self.f)
|
||||
|
||||
self.t = t
|
||||
assert self.t >= 0
|
||||
|
||||
if self.t > 0:
|
||||
print("#%d" % (10 * self.t - 5), file=self.f)
|
||||
for path in sorted(self.clocks.keys()):
|
||||
if self.clocks[path][1] == "posedge":
|
||||
print("b0 %s" % self.nets[path][0], file=self.f)
|
||||
elif self.clocks[path][1] == "negedge":
|
||||
print("b1 %s" % self.nets[path][0], file=self.f)
|
||||
|
||||
print("#%d" % (10 * self.t), file=self.f)
|
||||
print("1!", file=self.f)
|
||||
print("b%s t" % format(self.t, "032b"), file=self.f)
|
||||
|
||||
for path in sorted(self.clocks.keys()):
|
||||
if self.clocks[path][1] == "negedge":
|
||||
print("b0 %s" % self.nets[path][0], file=self.f)
|
||||
else:
|
||||
print("b1 %s" % self.nets[path][0], file=self.f)
|
||||
|
|
|
@ -42,7 +42,7 @@ struct SmvWorker
|
|||
|
||||
pool<Wire*> partial_assignment_wires;
|
||||
dict<SigBit, std::pair<const char*, int>> partial_assignment_bits;
|
||||
vector<string> assignments, invarspecs;
|
||||
vector<string> inputvars, vars, definitions, assignments, invarspecs;
|
||||
|
||||
const char *cid()
|
||||
{
|
||||
|
@ -195,7 +195,7 @@ struct SmvWorker
|
|||
return rvalue(sig);
|
||||
|
||||
const char *temp_id = cid();
|
||||
f << stringf(" %s : unsigned word[%d]; -- %s\n", temp_id, GetSize(sig), log_signal(sig));
|
||||
// f << stringf(" %s : unsigned word[%d]; -- %s\n", temp_id, GetSize(sig), log_signal(sig));
|
||||
|
||||
int offset = 0;
|
||||
for (auto bit : sig) {
|
||||
|
@ -210,14 +210,14 @@ struct SmvWorker
|
|||
void run()
|
||||
{
|
||||
f << stringf("MODULE %s\n", cid(module->name));
|
||||
f << stringf(" VAR\n");
|
||||
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
if (SigSpec(wire) != sigmap(wire))
|
||||
partial_assignment_wires.insert(wire);
|
||||
|
||||
f << stringf(" %s : unsigned word[%d]; -- %s\n", cid(wire->name), wire->width, log_id(wire));
|
||||
if (wire->port_input)
|
||||
inputvars.push_back(stringf("%s : unsigned word[%d]; -- %s", cid(wire->name), wire->width, log_id(wire)));
|
||||
|
||||
if (wire->attributes.count("\\init"))
|
||||
assignments.push_back(stringf("init(%s) := %s;", lvalue(wire), rvalue(wire->attributes.at("\\init"))));
|
||||
|
@ -275,8 +275,8 @@ struct SmvWorker
|
|||
const char *b_shr = rvalue_u(sig_b);
|
||||
const char *b_shl = cid();
|
||||
|
||||
f << stringf(" %s : unsigned word[%d]; -- neg(%s)\n", b_shl, GetSize(sig_b), log_signal(sig_b));
|
||||
assignments.push_back(stringf("%s := unsigned(-%s);", b_shl, rvalue_s(sig_b)));
|
||||
// f << stringf(" %s : unsigned word[%d]; -- neg(%s)\n", b_shl, GetSize(sig_b), log_signal(sig_b));
|
||||
definitions.push_back(stringf("%s := unsigned(-%s);", b_shl, rvalue_s(sig_b)));
|
||||
|
||||
string expr_shl = stringf("resize(%s << %s[%d:0], %d)", expr_a.c_str(), b_shl, shift_b_width-1, width_y);
|
||||
string expr_shr = stringf("resize(%s >> %s[%d:0], %d)", expr_a.c_str(), b_shr, shift_b_width-1, width_y);
|
||||
|
@ -303,7 +303,7 @@ struct SmvWorker
|
|||
GetSize(sig_b)-shift_b_width, width_y, expr.c_str());
|
||||
}
|
||||
|
||||
assignments.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str()));
|
||||
definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str()));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
@ -319,12 +319,12 @@ struct SmvWorker
|
|||
|
||||
if (cell->getParam("\\A_SIGNED").as_bool())
|
||||
{
|
||||
assignments.push_back(stringf("%s := unsigned(%s%s);", lvalue(cell->getPort("\\Y")),
|
||||
definitions.push_back(stringf("%s := unsigned(%s%s);", lvalue(cell->getPort("\\Y")),
|
||||
op.c_str(), rvalue_s(cell->getPort("\\A"), width)));
|
||||
}
|
||||
else
|
||||
{
|
||||
assignments.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")),
|
||||
definitions.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")),
|
||||
op.c_str(), rvalue_u(cell->getPort("\\A"), width)));
|
||||
}
|
||||
|
||||
|
@ -346,12 +346,12 @@ struct SmvWorker
|
|||
|
||||
if (cell->getParam("\\A_SIGNED").as_bool())
|
||||
{
|
||||
assignments.push_back(stringf("%s := unsigned(%s %s %s);", lvalue(cell->getPort("\\Y")),
|
||||
definitions.push_back(stringf("%s := unsigned(%s %s %s);", lvalue(cell->getPort("\\Y")),
|
||||
rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width)));
|
||||
}
|
||||
else
|
||||
{
|
||||
assignments.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")),
|
||||
definitions.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")),
|
||||
rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width)));
|
||||
}
|
||||
|
||||
|
@ -370,12 +370,12 @@ struct SmvWorker
|
|||
|
||||
if (cell->getParam("\\A_SIGNED").as_bool())
|
||||
{
|
||||
assignments.push_back(stringf("%s := resize(unsigned(%s %s %s), %d);", lvalue(cell->getPort("\\Y")),
|
||||
definitions.push_back(stringf("%s := resize(unsigned(%s %s %s), %d);", lvalue(cell->getPort("\\Y")),
|
||||
rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width), width_y));
|
||||
}
|
||||
else
|
||||
{
|
||||
assignments.push_back(stringf("%s := resize(%s %s %s, %d);", lvalue(cell->getPort("\\Y")),
|
||||
definitions.push_back(stringf("%s := resize(%s %s %s, %d);", lvalue(cell->getPort("\\Y")),
|
||||
rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width), width_y));
|
||||
}
|
||||
|
||||
|
@ -407,7 +407,7 @@ struct SmvWorker
|
|||
expr_b = stringf("resize(%s, %d)", rvalue(cell->getPort("\\B")), width);
|
||||
}
|
||||
|
||||
assignments.push_back(stringf("%s := resize(word1(%s %s %s), %d);", lvalue(cell->getPort("\\Y")),
|
||||
definitions.push_back(stringf("%s := resize(word1(%s %s %s), %d);", lvalue(cell->getPort("\\Y")),
|
||||
expr_a.c_str(), op.c_str(), expr_b.c_str(), GetSize(cell->getPort("\\Y"))));
|
||||
|
||||
continue;
|
||||
|
@ -425,7 +425,7 @@ struct SmvWorker
|
|||
if (cell->type == "$reduce_or") expr = stringf("%s != 0ub%d_0", expr_a, width_a);
|
||||
if (cell->type == "$reduce_bool") expr = stringf("%s != 0ub%d_0", expr_a, width_a);
|
||||
|
||||
assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
|
||||
definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -444,7 +444,7 @@ struct SmvWorker
|
|||
if (cell->type == "$reduce_xnor")
|
||||
expr = "!(" + expr + ")";
|
||||
|
||||
assignments.push_back(stringf("%s := resize(%s, %d);", expr_y, expr.c_str(), width_y));
|
||||
definitions.push_back(stringf("%s := resize(%s, %d);", expr_y, expr.c_str(), width_y));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -462,7 +462,7 @@ struct SmvWorker
|
|||
if (cell->type == "$logic_and") expr = expr_a + " & " + expr_b;
|
||||
if (cell->type == "$logic_or") expr = expr_a + " | " + expr_b;
|
||||
|
||||
assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
|
||||
definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -474,7 +474,7 @@ struct SmvWorker
|
|||
string expr_a = stringf("(%s = 0ub%d_0)", rvalue(cell->getPort("\\A")), width_a);
|
||||
const char *expr_y = lvalue(cell->getPort("\\Y"));
|
||||
|
||||
assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a.c_str(), width_y));
|
||||
definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a.c_str(), width_y));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -490,12 +490,13 @@ struct SmvWorker
|
|||
expr += stringf("bool(%s) ? %s : ", rvalue(sig_s[i]), rvalue(sig_b.extract(i*width, width)));
|
||||
expr += rvalue(sig_a);
|
||||
|
||||
assignments.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str()));
|
||||
definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str()));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$dff")
|
||||
{
|
||||
vars.push_back(stringf("%s : unsigned word[%d]; -- %s", lvalue(cell->getPort("\\Q")), GetSize(cell->getPort("\\Q")), log_signal(cell->getPort("\\Q"))));
|
||||
assignments.push_back(stringf("next(%s) := %s;", lvalue(cell->getPort("\\Q")), rvalue(cell->getPort("\\D"))));
|
||||
continue;
|
||||
}
|
||||
|
@ -503,59 +504,63 @@ struct SmvWorker
|
|||
if (cell->type.in("$_BUF_", "$_NOT_"))
|
||||
{
|
||||
string op = cell->type == "$_NOT_" ? "!" : "";
|
||||
assignments.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue(cell->getPort("\\A"))));
|
||||
definitions.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue(cell->getPort("\\A"))));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_"))
|
||||
if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_"))
|
||||
{
|
||||
string op;
|
||||
|
||||
if (cell->type.in("$_AND_", "$_NAND_")) op = "&";
|
||||
if (cell->type.in("$_OR_", "$_NOR_")) op = "|";
|
||||
if (cell->type.in("$_AND_", "$_NAND_", "$_ANDNOT_")) op = "&";
|
||||
if (cell->type.in("$_OR_", "$_NOR_", "$_ORNOT_")) op = "|";
|
||||
if (cell->type.in("$_XOR_")) op = "xor";
|
||||
if (cell->type.in("$_XNOR_")) op = "xnor";
|
||||
|
||||
if (cell->type.in("$_NAND_", "$_NOR_"))
|
||||
assignments.push_back(stringf("%s := !(%s %s %s);", lvalue(cell->getPort("\\Y")),
|
||||
if (cell->type.in("$_ANDNOT_", "$_ORNOT_"))
|
||||
definitions.push_back(stringf("%s := %s %s (!%s);", lvalue(cell->getPort("\\Y")),
|
||||
rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B"))));
|
||||
else
|
||||
assignments.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")),
|
||||
if (cell->type.in("$_NAND_", "$_NOR_"))
|
||||
definitions.push_back(stringf("%s := !(%s %s %s);", lvalue(cell->getPort("\\Y")),
|
||||
rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B"))));
|
||||
else
|
||||
definitions.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")),
|
||||
rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B"))));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$_MUX_")
|
||||
{
|
||||
assignments.push_back(stringf("%s := bool(%s) ? %s : %s;", lvalue(cell->getPort("\\Y")),
|
||||
definitions.push_back(stringf("%s := bool(%s) ? %s : %s;", lvalue(cell->getPort("\\Y")),
|
||||
rvalue(cell->getPort("\\S")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\A"))));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$_AOI3_")
|
||||
{
|
||||
assignments.push_back(stringf("%s := !((%s & %s) | %s);", lvalue(cell->getPort("\\Y")),
|
||||
definitions.push_back(stringf("%s := !((%s & %s) | %s);", lvalue(cell->getPort("\\Y")),
|
||||
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C"))));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$_OAI3_")
|
||||
{
|
||||
assignments.push_back(stringf("%s := !((%s | %s) & %s);", lvalue(cell->getPort("\\Y")),
|
||||
definitions.push_back(stringf("%s := !((%s | %s) & %s);", lvalue(cell->getPort("\\Y")),
|
||||
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C"))));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$_AOI4_")
|
||||
{
|
||||
assignments.push_back(stringf("%s := !((%s & %s) | (%s & %s));", lvalue(cell->getPort("\\Y")),
|
||||
definitions.push_back(stringf("%s := !((%s & %s) | (%s & %s));", lvalue(cell->getPort("\\Y")),
|
||||
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D"))));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cell->type == "$_OAI4_")
|
||||
{
|
||||
assignments.push_back(stringf("%s := !((%s | %s) & (%s | %s));", lvalue(cell->getPort("\\Y")),
|
||||
definitions.push_back(stringf("%s := !((%s | %s) & (%s | %s));", lvalue(cell->getPort("\\Y")),
|
||||
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D"))));
|
||||
continue;
|
||||
}
|
||||
|
@ -563,13 +568,13 @@ struct SmvWorker
|
|||
if (cell->type[0] == '$')
|
||||
log_error("Found currently unsupported cell type %s (%s.%s).\n", log_id(cell->type), log_id(module), log_id(cell));
|
||||
|
||||
f << stringf(" %s : %s;\n", cid(cell->name), cid(cell->type));
|
||||
// f << stringf(" %s : %s;\n", cid(cell->name), cid(cell->type));
|
||||
|
||||
for (auto &conn : cell->connections())
|
||||
if (cell->output(conn.first))
|
||||
assignments.push_back(stringf("%s := %s.%s;", lvalue(conn.second), cid(cell->name), cid(conn.first)));
|
||||
definitions.push_back(stringf("%s := %s.%s;", lvalue(conn.second), cid(cell->name), cid(conn.first)));
|
||||
else
|
||||
assignments.push_back(stringf("%s.%s := %s;", cid(cell->name), cid(conn.first), rvalue(conn.second)));
|
||||
definitions.push_back(stringf("%s.%s := %s;", cid(cell->name), cid(conn.first), rvalue(conn.second)));
|
||||
}
|
||||
|
||||
for (Wire *wire : partial_assignment_wires)
|
||||
|
@ -653,7 +658,25 @@ struct SmvWorker
|
|||
}
|
||||
}
|
||||
|
||||
assignments.push_back(stringf("%s := %s;", cid(wire->name), expr.c_str()));
|
||||
definitions.push_back(stringf("%s := %s;", cid(wire->name), expr.c_str()));
|
||||
}
|
||||
|
||||
if (!inputvars.empty()) {
|
||||
f << stringf(" IVAR\n");
|
||||
for (const string &line : inputvars)
|
||||
f << stringf(" %s\n", line.c_str());
|
||||
}
|
||||
|
||||
if (!vars.empty()) {
|
||||
f << stringf(" VAR\n");
|
||||
for (const string &line : vars)
|
||||
f << stringf(" %s\n", line.c_str());
|
||||
}
|
||||
|
||||
if (!definitions.empty()) {
|
||||
f << stringf(" DEFINE\n");
|
||||
for (const string &line : definitions)
|
||||
f << stringf(" %s\n", line.c_str());
|
||||
}
|
||||
|
||||
if (!assignments.empty()) {
|
||||
|
@ -671,7 +694,7 @@ struct SmvWorker
|
|||
|
||||
struct SmvBackend : public Backend {
|
||||
SmvBackend() : Backend("smv", "write design to SMV file") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -689,7 +712,7 @@ struct SmvBackend : public Backend {
|
|||
log("THIS COMMAND IS UNDER CONSTRUCTION\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::ifstream template_f;
|
||||
bool verbose = false;
|
||||
|
|
|
@ -132,7 +132,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De
|
|||
|
||||
struct SpiceBackend : public Backend {
|
||||
SpiceBackend() : Backend("spice", "write design to SPICE netlist file") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -161,7 +161,7 @@ struct SpiceBackend : public Backend {
|
|||
log(" set the specified module as design top module\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
std::string top_module_name;
|
||||
RTLIL::Module *top_module = NULL;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += backends/table/table.o
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/celltypes.h"
|
||||
#include "kernel/log.h"
|
||||
#include <string>
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct TableBackend : public Backend {
|
||||
TableBackend() : Backend("table", "write design as connectivity table") { }
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" write_table [options] [filename]\n");
|
||||
log("\n");
|
||||
log("Write the current design as connectivity table. The output is a tab-separated\n");
|
||||
log("ASCII table with the following columns:\n");
|
||||
log("\n");
|
||||
log(" module name\n");
|
||||
log(" cell name\n");
|
||||
log(" cell type\n");
|
||||
log(" cell port\n");
|
||||
log(" direction\n");
|
||||
log(" signal\n");
|
||||
log("\n");
|
||||
log("module inputs and outputs are output using cell type and port '-' and with\n");
|
||||
log("'pi' (primary input) or 'po' (primary output) or 'pio' as direction.\n");
|
||||
}
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing TABLE backend.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
// if (args[argidx] == "-top" && argidx+1 < args.size()) {
|
||||
// top_module_name = args[++argidx];
|
||||
// continue;
|
||||
// }
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
design->sort();
|
||||
|
||||
for (auto module : design->modules())
|
||||
{
|
||||
if (module->get_bool_attribute("\\blackbox"))
|
||||
continue;
|
||||
|
||||
SigMap sigmap(module);
|
||||
|
||||
for (auto wire : module->wires())
|
||||
{
|
||||
if (wire->port_id == 0)
|
||||
continue;
|
||||
|
||||
*f << log_id(module) << "\t";
|
||||
*f << log_id(wire) << "\t";
|
||||
*f << "-" << "\t";
|
||||
*f << "-" << "\t";
|
||||
|
||||
if (wire->port_input && wire->port_output)
|
||||
*f << "pio" << "\t";
|
||||
else if (wire->port_input)
|
||||
*f << "pi" << "\t";
|
||||
else if (wire->port_output)
|
||||
*f << "po" << "\t";
|
||||
else
|
||||
log_abort();
|
||||
|
||||
*f << log_signal(sigmap(wire)) << "\n";
|
||||
}
|
||||
|
||||
for (auto cell : module->cells())
|
||||
for (auto conn : cell->connections())
|
||||
{
|
||||
*f << log_id(module) << "\t";
|
||||
*f << log_id(cell) << "\t";
|
||||
*f << log_id(cell->type) << "\t";
|
||||
*f << log_id(conn.first) << "\t";
|
||||
|
||||
if (cell->input(conn.first) && cell->output(conn.first))
|
||||
*f << "inout" << "\t";
|
||||
else if (cell->input(conn.first))
|
||||
*f << "in" << "\t";
|
||||
else if (cell->output(conn.first))
|
||||
*f << "out" << "\t";
|
||||
else
|
||||
*f << "unknown" << "\t";
|
||||
|
||||
*f << log_signal(sigmap(conn.second)) << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
} TableBackend;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -33,13 +33,15 @@
|
|||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
bool verbose, norename, noattr, attr2comment, noexpr, nodec, nostr, defparam;
|
||||
bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, defparam, decimal;
|
||||
int auto_name_counter, auto_name_offset, auto_name_digits;
|
||||
std::map<RTLIL::IdString, int> auto_name_map;
|
||||
std::set<RTLIL::IdString> reg_wires, reg_ct;
|
||||
std::string auto_prefix;
|
||||
|
||||
RTLIL::Module *active_module;
|
||||
dict<RTLIL::SigBit, RTLIL::State> active_initdata;
|
||||
SigMap active_sigmap;
|
||||
|
||||
void reset_auto_counter_id(RTLIL::IdString id, bool may_rename)
|
||||
{
|
||||
|
@ -159,23 +161,73 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
|
|||
if (width < 0)
|
||||
width = data.bits.size() - offset;
|
||||
if (nostr)
|
||||
goto dump_bits;
|
||||
goto dump_hex;
|
||||
if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
|
||||
if (width == 32 && !no_decimal && !nodec) {
|
||||
int32_t val = 0;
|
||||
for (int i = offset+width-1; i >= offset; i--) {
|
||||
log_assert(i < (int)data.bits.size());
|
||||
if (data.bits[i] != RTLIL::S0 && data.bits[i] != RTLIL::S1)
|
||||
goto dump_bits;
|
||||
goto dump_hex;
|
||||
if (data.bits[i] == RTLIL::S1)
|
||||
val |= 1 << (i - offset);
|
||||
}
|
||||
if (set_signed && val < 0)
|
||||
if (decimal)
|
||||
f << stringf("%d", val);
|
||||
else if (set_signed && val < 0)
|
||||
f << stringf("-32'sd%u", -val);
|
||||
else
|
||||
f << stringf("32'%sd%u", set_signed ? "s" : "", val);
|
||||
} else {
|
||||
dump_bits:
|
||||
dump_hex:
|
||||
if (nohex)
|
||||
goto dump_bin;
|
||||
vector<char> bin_digits, hex_digits;
|
||||
for (int i = offset; i < offset+width; i++) {
|
||||
log_assert(i < (int)data.bits.size());
|
||||
switch (data.bits[i]) {
|
||||
case RTLIL::S0: bin_digits.push_back('0'); break;
|
||||
case RTLIL::S1: bin_digits.push_back('1'); break;
|
||||
case RTLIL::Sx: bin_digits.push_back('x'); break;
|
||||
case RTLIL::Sz: bin_digits.push_back('z'); break;
|
||||
case RTLIL::Sa: bin_digits.push_back('z'); break;
|
||||
case RTLIL::Sm: log_error("Found marker state in final netlist.");
|
||||
}
|
||||
}
|
||||
if (GetSize(bin_digits) == 0)
|
||||
goto dump_bin;
|
||||
while (GetSize(bin_digits) % 4 != 0)
|
||||
if (bin_digits.back() == '1')
|
||||
bin_digits.push_back('0');
|
||||
else
|
||||
bin_digits.push_back(bin_digits.back());
|
||||
for (int i = 0; i < GetSize(bin_digits); i += 4)
|
||||
{
|
||||
char bit_3 = bin_digits[i+3];
|
||||
char bit_2 = bin_digits[i+2];
|
||||
char bit_1 = bin_digits[i+1];
|
||||
char bit_0 = bin_digits[i+0];
|
||||
if (bit_3 == 'x' || bit_2 == 'x' || bit_1 == 'x' || bit_0 == 'x') {
|
||||
if (bit_3 != 'x' || bit_2 != 'x' || bit_1 != 'x' || bit_0 != 'x')
|
||||
goto dump_bin;
|
||||
hex_digits.push_back('x');
|
||||
continue;
|
||||
}
|
||||
if (bit_3 == 'z' || bit_2 == 'z' || bit_1 == 'z' || bit_0 == 'z') {
|
||||
if (bit_3 != 'z' || bit_2 != 'z' || bit_1 != 'z' || bit_0 != 'z')
|
||||
goto dump_bin;
|
||||
hex_digits.push_back('z');
|
||||
continue;
|
||||
}
|
||||
int val = 8*(bit_3 - '0') + 4*(bit_2 - '0') + 2*(bit_1 - '0') + (bit_0 - '0');
|
||||
hex_digits.push_back(val < 10 ? '0' + val : 'a' + val - 10);
|
||||
}
|
||||
f << stringf("%d'%sh", width, set_signed ? "s" : "");
|
||||
for (int i = GetSize(hex_digits)-1; i >= 0; i--)
|
||||
f << hex_digits[i];
|
||||
}
|
||||
if (0) {
|
||||
dump_bin:
|
||||
f << stringf("%d'%sb", width, set_signed ? "s" : "");
|
||||
if (width == 0)
|
||||
f << stringf("0");
|
||||
|
@ -214,6 +266,26 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
|
|||
}
|
||||
}
|
||||
|
||||
void dump_reg_init(std::ostream &f, SigSpec sig)
|
||||
{
|
||||
Const initval;
|
||||
bool gotinit = false;
|
||||
|
||||
for (auto bit : active_sigmap(sig)) {
|
||||
if (active_initdata.count(bit)) {
|
||||
initval.bits.push_back(active_initdata.at(bit));
|
||||
gotinit = true;
|
||||
} else {
|
||||
initval.bits.push_back(State::Sx);
|
||||
}
|
||||
}
|
||||
|
||||
if (gotinit) {
|
||||
f << " = ";
|
||||
dump_const(f, initval);
|
||||
}
|
||||
}
|
||||
|
||||
void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decimal = false)
|
||||
{
|
||||
if (chunk.wire == NULL) {
|
||||
|
@ -302,12 +374,12 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
|
|||
if (wire->port_input && wire->port_output)
|
||||
f << stringf("%s" "inout%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
|
||||
if (reg_wires.count(wire->name)) {
|
||||
f << stringf("%s" "reg%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
|
||||
f << stringf("%s" "reg%s %s", indent.c_str(), range.c_str(), id(wire->name).c_str());
|
||||
if (wire->attributes.count("\\init")) {
|
||||
f << stringf("%s" "initial %s = ", indent.c_str(), id(wire->name).c_str());
|
||||
f << stringf(" = ");
|
||||
dump_const(f, wire->attributes.at("\\init"));
|
||||
f << stringf(";\n");
|
||||
}
|
||||
f << stringf(";\n");
|
||||
} else if (!wire->port_input && !wire->port_output)
|
||||
f << stringf("%s" "wire%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
|
||||
#endif
|
||||
|
@ -316,7 +388,7 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
|
|||
void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory)
|
||||
{
|
||||
dump_attributes(f, indent, memory->attributes);
|
||||
f << stringf("%s" "reg [%d:0] %s [%d:0];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size-1);
|
||||
f << stringf("%s" "reg [%d:0] %s [%d:%d];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size+memory->start_offset-1, memory->start_offset);
|
||||
}
|
||||
|
||||
void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true)
|
||||
|
@ -400,7 +472,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_")) {
|
||||
if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_")) {
|
||||
f << stringf("%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->getPort("\\Y"));
|
||||
f << stringf(" = ");
|
||||
|
@ -408,16 +480,18 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
f << stringf("~(");
|
||||
dump_cell_expr_port(f, cell, "A", false);
|
||||
f << stringf(" ");
|
||||
if (cell->type.in("$_AND_", "$_NAND_"))
|
||||
if (cell->type.in("$_AND_", "$_NAND_", "$_ANDNOT_"))
|
||||
f << stringf("&");
|
||||
if (cell->type.in("$_OR_", "$_NOR_"))
|
||||
if (cell->type.in("$_OR_", "$_NOR_", "$_ORNOT_"))
|
||||
f << stringf("|");
|
||||
if (cell->type.in("$_XOR_", "$_XNOR_"))
|
||||
f << stringf("^");
|
||||
dump_attributes(f, "", cell->attributes, ' ');
|
||||
f << stringf(" ");
|
||||
if (cell->type.in("$_ANDNOT_", "$_ORNOT_"))
|
||||
f << stringf("~(");
|
||||
dump_cell_expr_port(f, cell, "B", false);
|
||||
if (cell->type.in("$_NAND_", "$_NOR_", "$_XNOR_"))
|
||||
if (cell->type.in("$_NAND_", "$_NOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_"))
|
||||
f << stringf(")");
|
||||
f << stringf(";\n");
|
||||
return true;
|
||||
|
@ -474,8 +548,11 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
std::string reg_name = cellname(cell);
|
||||
bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
|
||||
|
||||
if (!out_is_reg_wire)
|
||||
f << stringf("%s" "reg %s;\n", indent.c_str(), reg_name.c_str());
|
||||
if (!out_is_reg_wire) {
|
||||
f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
|
||||
dump_reg_init(f, cell->getPort("\\Q"));
|
||||
f << ";\n";
|
||||
}
|
||||
|
||||
dump_attributes(f, indent, cell->attributes);
|
||||
f << stringf("%s" "always @(%sedge ", indent.c_str(), cell->type[6] == 'P' ? "pos" : "neg");
|
||||
|
@ -514,8 +591,11 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
std::string reg_name = cellname(cell);
|
||||
bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
|
||||
|
||||
if (!out_is_reg_wire)
|
||||
f << stringf("%s" "reg %s;\n", indent.c_str(), reg_name.c_str());
|
||||
if (!out_is_reg_wire) {
|
||||
f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
|
||||
dump_reg_init(f, cell->getPort("\\Q"));
|
||||
f << ";\n";
|
||||
}
|
||||
|
||||
dump_attributes(f, indent, cell->attributes);
|
||||
f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_c == 'P' ? "pos" : "neg");
|
||||
|
@ -598,6 +678,52 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
#undef HANDLE_UNIOP
|
||||
#undef HANDLE_BINOP
|
||||
|
||||
if (cell->type == "$shift")
|
||||
{
|
||||
f << stringf("%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->getPort("\\Y"));
|
||||
f << stringf(" = ");
|
||||
if (cell->getParam("\\B_SIGNED").as_bool())
|
||||
{
|
||||
f << stringf("$signed(");
|
||||
dump_sigspec(f, cell->getPort("\\B"));
|
||||
f << stringf(")");
|
||||
f << stringf(" < 0 ? ");
|
||||
dump_sigspec(f, cell->getPort("\\A"));
|
||||
f << stringf(" << - ");
|
||||
dump_sigspec(f, cell->getPort("\\B"));
|
||||
f << stringf(" : ");
|
||||
dump_sigspec(f, cell->getPort("\\A"));
|
||||
f << stringf(" >> ");
|
||||
dump_sigspec(f, cell->getPort("\\B"));
|
||||
}
|
||||
else
|
||||
{
|
||||
dump_sigspec(f, cell->getPort("\\A"));
|
||||
f << stringf(" >> ");
|
||||
dump_sigspec(f, cell->getPort("\\B"));
|
||||
}
|
||||
f << stringf(";\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cell->type == "$shiftx")
|
||||
{
|
||||
f << stringf("%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->getPort("\\Y"));
|
||||
f << stringf(" = ");
|
||||
dump_sigspec(f, cell->getPort("\\A"));
|
||||
f << stringf("[");
|
||||
if (cell->getParam("\\B_SIGNED").as_bool())
|
||||
f << stringf("$signed(");
|
||||
dump_sigspec(f, cell->getPort("\\B"));
|
||||
if (cell->getParam("\\B_SIGNED").as_bool())
|
||||
f << stringf(")");
|
||||
f << stringf(" +: %d", cell->getParam("\\Y_WIDTH").as_int());
|
||||
f << stringf("];\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cell->type == "$mux")
|
||||
{
|
||||
f << stringf("%s" "assign ", indent.c_str());
|
||||
|
@ -682,6 +808,19 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (cell->type == "$lut")
|
||||
{
|
||||
f << stringf("%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->getPort("\\Y"));
|
||||
f << stringf(" = ");
|
||||
dump_const(f, cell->parameters.at("\\LUT"));
|
||||
f << stringf(" >> ");
|
||||
dump_attributes(f, "", cell->attributes, ' ');
|
||||
dump_sigspec(f, cell->getPort("\\A"));
|
||||
f << stringf(";\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cell->type == "$dffsr")
|
||||
{
|
||||
SigSpec sig_clk = cell->getPort("\\CLK");
|
||||
|
@ -698,8 +837,11 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
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());
|
||||
if (!out_is_reg_wire) {
|
||||
f << stringf("%s" "reg [%d:0] %s", indent.c_str(), width-1, reg_name.c_str());
|
||||
dump_reg_init(f, sig_q);
|
||||
f << ";\n";
|
||||
}
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
|
||||
|
@ -754,8 +896,11 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
std::string reg_name = cellname(cell);
|
||||
bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
|
||||
|
||||
if (!out_is_reg_wire)
|
||||
f << stringf("%s" "reg [%d:0] %s;\n", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str());
|
||||
if (!out_is_reg_wire) {
|
||||
f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str());
|
||||
dump_reg_init(f, cell->getPort("\\Q"));
|
||||
f << ";\n";
|
||||
}
|
||||
|
||||
f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
|
||||
dump_sigspec(f, sig_clk);
|
||||
|
@ -794,27 +939,64 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (cell->type == "$dlatch")
|
||||
{
|
||||
RTLIL::SigSpec sig_en;
|
||||
bool pol_en = false;
|
||||
|
||||
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);
|
||||
|
||||
if (!out_is_reg_wire) {
|
||||
f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str());
|
||||
dump_reg_init(f, cell->getPort("\\Q"));
|
||||
f << ";\n";
|
||||
}
|
||||
|
||||
f << stringf("%s" "always @*\n", indent.c_str());
|
||||
|
||||
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");
|
||||
|
||||
if (!out_is_reg_wire) {
|
||||
f << stringf("%s" "assign ", indent.c_str());
|
||||
dump_sigspec(f, cell->getPort("\\Q"));
|
||||
f << stringf(" = %s;\n", reg_name.c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cell->type == "$mem")
|
||||
{
|
||||
RTLIL::IdString memid = cell->parameters["\\MEMID"].decode_string();
|
||||
std::string mem_id = id(cell->parameters["\\MEMID"].decode_string());
|
||||
int abits = cell->parameters["\\ABITS"].as_int();
|
||||
int size = cell->parameters["\\SIZE"].as_int();
|
||||
int offset = cell->parameters["\\OFFSET"].as_int();
|
||||
int width = cell->parameters["\\WIDTH"].as_int();
|
||||
bool use_init = !(RTLIL::SigSpec(cell->parameters["\\INIT"]).is_fully_undef());
|
||||
|
||||
// for memory block make something like:
|
||||
// reg [7:0] memid [3:0];
|
||||
// initial begin
|
||||
// memid[0] <= ...
|
||||
// memid[0] = ...
|
||||
// end
|
||||
f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size-1, 0);
|
||||
f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size+offset-1, offset);
|
||||
if (use_init)
|
||||
{
|
||||
f << stringf("%s" "initial begin\n", indent.c_str());
|
||||
for (int i=0; i<size; i++)
|
||||
{
|
||||
f << stringf("%s" " %s[%d] <= ", indent.c_str(), mem_id.c_str(), i);
|
||||
f << stringf("%s" " %s[%d] = ", indent.c_str(), mem_id.c_str(), i);
|
||||
dump_const(f, cell->parameters["\\INIT"].extract(i*width, width));
|
||||
f << stringf(";\n");
|
||||
}
|
||||
|
@ -912,7 +1094,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
int nwrite_ports = cell->parameters["\\WR_PORTS"].as_int();
|
||||
RTLIL::SigSpec sig_wr_clk, sig_wr_data, sig_wr_addr, sig_wr_en;
|
||||
bool wr_clk_posedge;
|
||||
SigMap sigmap(active_module);
|
||||
|
||||
// write ports
|
||||
for (int i=0; i < nwrite_ports; i++)
|
||||
{
|
||||
|
@ -937,7 +1119,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
|||
int start_i = i, width = 1;
|
||||
SigBit wen_bit = sig_wr_en[i];
|
||||
|
||||
while (i+1 < GetSize(sig_wr_en) && sigmap(sig_wr_en[i+1]) == sigmap(wen_bit))
|
||||
while (i+1 < GetSize(sig_wr_en) && active_sigmap(sig_wr_en[i+1]) == active_sigmap(wen_bit))
|
||||
i++, width++;
|
||||
|
||||
if (wen_bit == State::S0)
|
||||
|
@ -1199,6 +1381,8 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo
|
|||
|
||||
if (sync->type == RTLIL::STa) {
|
||||
f << stringf("%s" "always @* begin\n", indent.c_str());
|
||||
} else if (sync->type == RTLIL::STi) {
|
||||
f << stringf("%s" "initial begin\n", indent.c_str());
|
||||
} else {
|
||||
f << stringf("%s" "always @(", indent.c_str());
|
||||
if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1)
|
||||
|
@ -1251,12 +1435,22 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
|
|||
reg_wires.clear();
|
||||
reset_auto_counter(module);
|
||||
active_module = module;
|
||||
active_sigmap.set(module);
|
||||
active_initdata.clear();
|
||||
|
||||
for (auto wire : module->wires())
|
||||
if (wire->attributes.count("\\init")) {
|
||||
SigSpec sig = active_sigmap(wire);
|
||||
Const val = wire->attributes.at("\\init");
|
||||
for (int i = 0; i < GetSize(sig) && i < GetSize(val); i++)
|
||||
active_initdata[sig[i]] = val.bits.at(i);
|
||||
}
|
||||
|
||||
if (!module->processes.empty())
|
||||
log_warning("Module %s contains unmapped RTLIL proccesses. RTLIL processes\n"
|
||||
log_warning("Module %s contains unmapped RTLIL processes. RTLIL processes\n"
|
||||
"can't always be mapped directly to Verilog always blocks. Unintended\n"
|
||||
"changes in simulation behavior are possible! Use \"proc\" to convert\n"
|
||||
"processes to logic networks and registers.", log_id(module));
|
||||
"processes to logic networks and registers.\n", log_id(module));
|
||||
|
||||
f << stringf("\n");
|
||||
for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
|
||||
|
@ -1327,11 +1521,13 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
|
|||
|
||||
f << stringf("%s" "endmodule\n", indent.c_str());
|
||||
active_module = NULL;
|
||||
active_sigmap.clear();
|
||||
active_initdata.clear();
|
||||
}
|
||||
|
||||
struct VerilogBackend : public Backend {
|
||||
VerilogBackend() : Backend("verilog", "write design to Verilog file") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -1359,13 +1555,21 @@ struct VerilogBackend : public Backend {
|
|||
log("\n");
|
||||
log(" -nodec\n");
|
||||
log(" 32-bit constant values are by default dumped as decimal numbers,\n");
|
||||
log(" not bit pattern. This option decativates this feature and instead\n");
|
||||
log(" not bit pattern. This option deactivates this feature and instead\n");
|
||||
log(" will write out all constants in binary.\n");
|
||||
log("\n");
|
||||
log(" -decimal\n");
|
||||
log(" dump 32-bit constants in decimal and without size and radix\n");
|
||||
log("\n");
|
||||
log(" -nohex\n");
|
||||
log(" constant values that are compatible with hex output are usually\n");
|
||||
log(" dumped as hex values. This option deactivates this feature and\n");
|
||||
log(" instead will write out all constants in binary.\n");
|
||||
log("\n");
|
||||
log(" -nostr\n");
|
||||
log(" Parameters and attributes that are specified as strings in the\n");
|
||||
log(" original input will be output as strings by this back-end. This\n");
|
||||
log(" decativates this feature and instead will write string constants\n");
|
||||
log(" deactivates this feature and instead will write string constants\n");
|
||||
log(" as binary numbers.\n");
|
||||
log("\n");
|
||||
log(" -defparam\n");
|
||||
|
@ -1391,7 +1595,7 @@ struct VerilogBackend : public Backend {
|
|||
log("this command is called on a design with RTLIL processes.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
log_header(design, "Executing Verilog backend.\n");
|
||||
|
||||
|
@ -1401,8 +1605,10 @@ struct VerilogBackend : public Backend {
|
|||
attr2comment = false;
|
||||
noexpr = false;
|
||||
nodec = false;
|
||||
nohex = false;
|
||||
nostr = false;
|
||||
defparam = false;
|
||||
decimal = false;
|
||||
auto_prefix = "";
|
||||
|
||||
bool blackboxes = false;
|
||||
|
@ -1412,6 +1618,8 @@ struct VerilogBackend : public Backend {
|
|||
|
||||
reg_ct.insert("$dff");
|
||||
reg_ct.insert("$adff");
|
||||
reg_ct.insert("$dffe");
|
||||
reg_ct.insert("$dlatch");
|
||||
|
||||
reg_ct.insert("$_DFF_N_");
|
||||
reg_ct.insert("$_DFF_P_");
|
||||
|
@ -1461,6 +1669,10 @@ struct VerilogBackend : public Backend {
|
|||
nodec = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-nohex") {
|
||||
nohex = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-nostr") {
|
||||
nostr = true;
|
||||
continue;
|
||||
|
@ -1469,6 +1681,10 @@ struct VerilogBackend : public Backend {
|
|||
defparam = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-decimal") {
|
||||
decimal = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-blackboxes") {
|
||||
blackboxes = true;
|
||||
continue;
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
demo.aig
|
||||
demo.aim
|
||||
demo.aiw
|
||||
demo.smt2
|
||||
demo.vcd
|
|
@ -0,0 +1,22 @@
|
|||
AIGER is a format for And-Inverter Graphs (AIGs).
|
||||
See http://fmv.jku.at/aiger/ for details.
|
||||
|
||||
AIGER is used in the Hardware Model Checking Competition (HWMCC),
|
||||
therefore all solvers competing in the competition have to support
|
||||
the format.
|
||||
|
||||
The example in this directory is using super_prove as solver. Check
|
||||
http://downloads.bvsrc.org/super_prove/ for the lates release. (See
|
||||
https://bitbucket.org/sterin/super_prove_build for sources.)
|
||||
|
||||
The "demo.sh" script in this directory expects a "super_prove" executable
|
||||
in the PATH. E.g. extract the release to /usr/local/libexec/super_prove
|
||||
and then create a /usr/local/bin/super_prove file with the following
|
||||
contents (and "chmod +x" that file):
|
||||
|
||||
#!/bin/bash
|
||||
exec /usr/local/libexec/super_prove/bin/super_prove.sh "$@"
|
||||
|
||||
The "demo.sh" script also expects the "z3" SMT2 solver in the PATH for
|
||||
converting the witness file generated by super_prove to VCD using
|
||||
yosys-smtbmc. See https://github.com/Z3Prover/z3 for install notes.
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/bash
|
||||
set -ex
|
||||
yosys -p '
|
||||
read_verilog -formal demo.v
|
||||
prep -flatten -nordff -top demo
|
||||
write_smt2 -wires demo.smt2
|
||||
flatten demo; delete -output
|
||||
memory_map; opt -full
|
||||
techmap; opt -fast
|
||||
abc -fast -g AND; opt_clean
|
||||
write_aiger -map demo.aim demo.aig
|
||||
'
|
||||
super_prove demo.aig > demo.aiw
|
||||
yosys-smtbmc --dump-vcd demo.vcd --aig demo demo.smt2
|
|
@ -0,0 +1,12 @@
|
|||
module demo(input clk, reset, ctrl);
|
||||
localparam NBITS = 10;
|
||||
reg [NBITS-1:0] counter;
|
||||
initial counter[NBITS-2] = 0;
|
||||
initial counter[0] = 1;
|
||||
always @(posedge clk) begin
|
||||
counter <= reset ? 1 : ctrl ? counter + 1 : counter - 1;
|
||||
assume(counter != 0);
|
||||
assume(counter != 1 << (NBITS-1));
|
||||
assert(counter != (1 << NBITS)-1);
|
||||
end
|
||||
endmodule
|
|
@ -19,3 +19,6 @@ set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN L1 } [get_ports {LD[15]}]
|
|||
|
||||
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports CLK]
|
||||
|
||||
set_property CONFIG_VOLTAGE 3.3 [current_design]
|
||||
set_property CFGBVS VCCO [current_design]
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
open_hw
|
||||
connect_hw_server
|
||||
open_hw_target [lindex [get_hw_targets] 0]
|
||||
set_property PROGRAM.FILE example.bit [lindex [get_hw_devices] 0]
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
|
||||
read_verilog counter.v
|
||||
read_verilog -lib cmos_cells.v
|
||||
|
||||
proc;; memory;; techmap;;
|
||||
|
||||
synth
|
||||
dfflibmap -liberty cmos_cells.lib
|
||||
abc -liberty cmos_cells.lib;;
|
||||
abc -liberty cmos_cells.lib
|
||||
opt_clean
|
||||
|
||||
stat -liberty cmos_cells.lib
|
||||
|
||||
# http://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/cadence/lib/tsmc025/signalstorm/osu025_stdcells.lib
|
||||
# dfflibmap -liberty osu025_stdcells.lib
|
||||
|
@ -13,4 +14,3 @@ abc -liberty cmos_cells.lib;;
|
|||
|
||||
write_verilog synth.v
|
||||
write_spice synth.sp
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ struct EvalDemoPass : public Pass
|
|||
{
|
||||
EvalDemoPass() : Pass("evaldemo") { }
|
||||
|
||||
virtual void execute(vector<string>, Design *design)
|
||||
void execute(vector<string>, Design *design) YS_OVERRIDE
|
||||
{
|
||||
Module *module = design->top_module();
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
demo.bit
|
||||
demo.out
|
||||
demo.rpt
|
||||
demo_syn.v
|
||||
demo_out.v
|
||||
demo_tr.html
|
||||
testbench
|
||||
testbench.vcd
|
|
@ -0,0 +1,17 @@
|
|||
Simple test project for Gowinsemi GW2A-55K Eval Board Mini.
|
||||
|
||||
Follow the install instructions for the Gowinsemi tools below,
|
||||
then run "bash run.sh" in this directory.
|
||||
|
||||
|
||||
Install instructions for gowinTool_linux
|
||||
----------------------------------------
|
||||
|
||||
1.) extract gowinTool_linux.zip
|
||||
|
||||
2.) set GOWIN_HOME env variable to the full path to the
|
||||
gowinTool_linux directory
|
||||
|
||||
3.) edit gowinTool_linux/bin/gwlicense.ini. Set lic="..." to
|
||||
the full path to the license file.
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// 50 MHz Clock
|
||||
IO_LOC "clk" D11;
|
||||
|
||||
// LEDs
|
||||
IO_LOC "leds[0]" D22;
|
||||
IO_LOC "leds[1]" E22;
|
||||
IO_LOC "leds[2]" G22;
|
||||
IO_LOC "leds[3]" J22;
|
||||
IO_LOC "leds[4]" L22;
|
||||
IO_LOC "leds[5]" L19;
|
||||
IO_LOC "leds[6]" L20;
|
||||
IO_LOC "leds[7]" M21;
|
||||
IO_LOC "leds[8]" N19;
|
||||
IO_LOC "leds[9]" R19;
|
||||
IO_LOC "leds[10]" T18;
|
||||
IO_LOC "leds[11]" AA22;
|
||||
IO_LOC "leds[12]" U18;
|
||||
IO_LOC "leds[13]" V20;
|
||||
IO_LOC "leds[14]" AA21;
|
||||
IO_LOC "leds[15]" AB21;
|
||||
|
||||
|
||||
// 7-Segment Display
|
||||
IO_LOC "seg7dig[0]" E20;
|
||||
IO_LOC "seg7dig[1]" G18;
|
||||
IO_LOC "seg7dig[2]" G20;
|
||||
IO_LOC "seg7dig[3]" F21;
|
||||
IO_LOC "seg7dig[4]" J20;
|
||||
IO_LOC "seg7dig[5]" H21;
|
||||
IO_LOC "seg7dig[6]" H18;
|
||||
IO_LOC "seg7dig[7]" D20;
|
||||
IO_LOC "seg7sel[0]" C19;
|
||||
IO_LOC "seg7sel[1]" B22;
|
||||
IO_LOC "seg7sel[2]" C20;
|
||||
IO_LOC "seg7sel[3]" C21;
|
||||
|
||||
// Switches
|
||||
IO_LOC "sw[0]" AB20;
|
||||
IO_LOC "sw[1]" AB19;
|
||||
IO_LOC "sw[2]" AB18;
|
||||
IO_LOC "sw[3]" AB17;
|
|
@ -0,0 +1 @@
|
|||
create_clock -name clk -period 20 -waveform {0 10} [get_ports {clk}]
|
|
@ -0,0 +1,12 @@
|
|||
module demo (
|
||||
input clk,
|
||||
input [3:0] sw,
|
||||
output [15:0] leds,
|
||||
output [7:0] seg7dig,
|
||||
output [3:0] seg7sel
|
||||
);
|
||||
localparam PRESCALE = 20;
|
||||
reg [PRESCALE+3:0] counter = 0;
|
||||
always @(posedge clk) counter <= counter + 1;
|
||||
assign leds = 1 << counter[PRESCALE +: 4];
|
||||
endmodule
|
|
@ -0,0 +1,12 @@
|
|||
#!/bin/bash
|
||||
set -ex
|
||||
yosys -p "synth_gowin -top demo -vout demo_syn.v" demo.v
|
||||
$GOWIN_HOME/bin/gowin -d demo_syn.v -cst demo.cst -sdc demo.sdc -p GW2A55-PBGA484-6 \
|
||||
-warning_all -out demo_out.v -rpt demo.rpt -tr demo_tr.html -bit demo.bit
|
||||
|
||||
# post place&route simulation (icarus verilog)
|
||||
if false; then
|
||||
iverilog -D POST_IMPL -o testbench -s testbench testbench.v \
|
||||
demo_out.v $(yosys-config --datdir/gowin/cells_sim.v)
|
||||
vvp -N testbench
|
||||
fi
|
|
@ -0,0 +1,40 @@
|
|||
module testbench;
|
||||
reg clk;
|
||||
|
||||
initial begin
|
||||
#5 clk = 0;
|
||||
forever #5 clk = ~clk;
|
||||
end
|
||||
|
||||
wire [15:0] leds;
|
||||
|
||||
initial begin
|
||||
// $dumpfile("testbench.vcd");
|
||||
// $dumpvars(0, testbench);
|
||||
$monitor("%b", leds);
|
||||
end
|
||||
|
||||
demo uut (
|
||||
.clk (clk ),
|
||||
`ifdef POST_IMPL
|
||||
.\leds[0] (leds[0]),
|
||||
.\leds[1] (leds[1]),
|
||||
.\leds[2] (leds[2]),
|
||||
.\leds[3] (leds[3]),
|
||||
.\leds[4] (leds[4]),
|
||||
.\leds[5] (leds[5]),
|
||||
.\leds[6] (leds[6]),
|
||||
.\leds[7] (leds[7]),
|
||||
.\leds[8] (leds[8]),
|
||||
.\leds[9] (leds[9]),
|
||||
.\leds[10] (leds[10]),
|
||||
.\leds[11] (leds[11]),
|
||||
.\leds[12] (leds[12]),
|
||||
.\leds[13] (leds[13]),
|
||||
.\leds[14] (leds[14]),
|
||||
.\leds[15] (leds[15])
|
||||
`else
|
||||
.leds(leds)
|
||||
`endif
|
||||
);
|
||||
endmodule
|
|
@ -0,0 +1,3 @@
|
|||
/netlist.edn
|
||||
/netlist.v
|
||||
/work
|
|
@ -0,0 +1,22 @@
|
|||
module top (
|
||||
input clk,
|
||||
output LED1,
|
||||
output LED2,
|
||||
output LED3,
|
||||
output LED4,
|
||||
output LED5
|
||||
);
|
||||
|
||||
localparam BITS = 5;
|
||||
localparam LOG2DELAY = 22;
|
||||
|
||||
reg [BITS+LOG2DELAY-1:0] counter = 0;
|
||||
reg [BITS-1:0] outcnt;
|
||||
|
||||
always @(posedge clk) begin
|
||||
counter <= counter + 1;
|
||||
outcnt <= counter >> LOG2DELAY;
|
||||
end
|
||||
|
||||
assign {LED1, LED2, LED3, LED4, LED5} = outcnt ^ (outcnt >> 1);
|
||||
endmodule
|
|
@ -0,0 +1,3 @@
|
|||
read_verilog example.v
|
||||
synth_sf2 -top top -edif netlist.edn
|
||||
write_verilog netlist.v
|
|
@ -0,0 +1,35 @@
|
|||
# Run with "libero SCRIPT:libero.tcl"
|
||||
|
||||
new_project \
|
||||
-name top \
|
||||
-location work \
|
||||
-family IGLOO2 \
|
||||
-die PA4MGL500 \
|
||||
-package tq144 \
|
||||
-speed -1 \
|
||||
-hdl VERILOG
|
||||
|
||||
# import_files -edif "[pwd]/netlist.edn"
|
||||
|
||||
import_files -hdl_source "[pwd]/netlist.v"
|
||||
set_root top
|
||||
|
||||
save_project
|
||||
|
||||
puts "**> SYNTHESIZE"
|
||||
run_tool -name {SYNTHESIZE}
|
||||
puts "<** SYNTHESIZE"
|
||||
|
||||
puts "**> COMPILE"
|
||||
run_tool -name {COMPILE}
|
||||
puts "<** COMPILE"
|
||||
|
||||
puts "**> PLACEROUTE"
|
||||
run_tool -name {PLACEROUTE}
|
||||
puts "<** PLACEROUTE"
|
||||
|
||||
# puts "**> export_bitstream"
|
||||
# export_bitstream_file -trusted_facility_file 1 -trusted_facility_file_components {FABRIC}
|
||||
# puts "<** export_bitstream"
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
set -ex
|
||||
rm -rf work
|
||||
yosys example.ys
|
||||
LM_LICENSE_FILE=1702@`hostname` /opt/microsemi/Libero_SoC_v11.9/Libero/bin/libero SCRIPT:libero.tcl
|
|
@ -0,0 +1,4 @@
|
|||
QUARTUS_VERSION = "16.1"
|
||||
# Revisions
|
||||
|
||||
PROJECT_REVISION = "de2i"
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
export REV="de2i"
|
||||
|
||||
quartus_map -c $REV top && \
|
||||
quartus_fit -c $REV top && \
|
||||
quartus_asm -c $REV top
|
|
@ -0,0 +1,2 @@
|
|||
#/bin/env bash
|
||||
yosys -p "synth_intel -family cycloneiv -top top -vqm top.vqm" top.v sevenseg.v
|
|
@ -0,0 +1,25 @@
|
|||
module sevenseg ( output reg [6:0] HEX0,
|
||||
input [3:0] SW );
|
||||
|
||||
always @(*) begin
|
||||
case(SW)
|
||||
4'h1: HEX0 = 7'b1111001;
|
||||
4'h2: HEX0 = 7'b0100100;
|
||||
4'h3: HEX0 = 7'b0110000;
|
||||
4'h4: HEX0 = 7'b0011001;
|
||||
4'h5: HEX0 = 7'b0010010;
|
||||
4'h6: HEX0 = 7'b0000010;
|
||||
4'h7: HEX0 = 7'b1111000;
|
||||
4'h8: HEX0 = 7'b0000000;
|
||||
4'h9: HEX0 = 7'b0011000;
|
||||
4'ha: HEX0 = 7'b0001000;
|
||||
4'hb: HEX0 = 7'b0000011;
|
||||
4'hc: HEX0 = 7'b1000110;
|
||||
4'hd: HEX0 = 7'b0100001;
|
||||
4'he: HEX0 = 7'b0000110;
|
||||
4'hf: HEX0 = 7'b0001110;
|
||||
4'h0: HEX0 = 7'b1000000;
|
||||
endcase // case (SW)
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,15 @@
|
|||
`default_nettype none
|
||||
module top ( output wire [6:0] HEX0, HEX1, HEX2, HEX3, HEX4, HEX5, HEX6, HEX7,
|
||||
input wire [15:0] SW );
|
||||
|
||||
|
||||
sevenseg UUD0 (.HEX0(HEX0), .SW(4'h7));
|
||||
sevenseg UUD1 (.HEX0(HEX1), .SW(4'h1));
|
||||
sevenseg UUD2 (.HEX0(HEX2), .SW(4'h0));
|
||||
sevenseg UUD3 (.HEX0(HEX3), .SW(4'h2));
|
||||
sevenseg UUD4 (.HEX0(HEX4), .SW(SW[3:0]));
|
||||
sevenseg UUD5 (.HEX0(HEX5), .SW(SW[7:4]));
|
||||
sevenseg UUD6 (.HEX0(HEX6), .SW(SW[11:8]));
|
||||
sevenseg UUD7 (.HEX0(HEX7), .SW(SW[15:12]));
|
||||
|
||||
endmodule
|
|
@ -0,0 +1 @@
|
|||
yosys -p "synth_intel -family max10 -top top -vqm top.vqm" top.v sevenseg.v
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
iverilog -D POST_IMPL -o verif_post -s tb_top tb_top.v top.vqm $(yosys-config --datdir/altera_intel/max10/cells_comb_max10.v)
|
||||
vvp -N verif_post
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
module sevenseg ( output reg [6:0] HEX0,
|
||||
input [3:0] SW );
|
||||
|
||||
always @(*) begin
|
||||
case(SW)
|
||||
4'h1: HEX0 = 7'b1111001;
|
||||
4'h2: HEX0 = 7'b0100100;
|
||||
4'h3: HEX0 = 7'b0110000;
|
||||
4'h4: HEX0 = 7'b0011001;
|
||||
4'h5: HEX0 = 7'b0010010;
|
||||
4'h6: HEX0 = 7'b0000010;
|
||||
4'h7: HEX0 = 7'b1111000;
|
||||
4'h8: HEX0 = 7'b0000000;
|
||||
4'h9: HEX0 = 7'b0011000;
|
||||
4'ha: HEX0 = 7'b0001000;
|
||||
4'hb: HEX0 = 7'b0000011;
|
||||
4'hc: HEX0 = 7'b1000110;
|
||||
4'hd: HEX0 = 7'b0100001;
|
||||
4'he: HEX0 = 7'b0000110;
|
||||
4'hf: HEX0 = 7'b0001110;
|
||||
4'h0: HEX0 = 7'b1000000;
|
||||
endcase // case (SW)
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,15 @@
|
|||
`default_nettype none
|
||||
module top ( output wire [6:0] HEX0, HEX1, HEX2, HEX3, HEX4, HEX5, HEX6, HEX7,
|
||||
input wire [15:0] SW );
|
||||
|
||||
|
||||
sevenseg UUD0 (.HEX0(HEX0), .SW(4'h7));
|
||||
sevenseg UUD1 (.HEX0(HEX1), .SW(4'h1));
|
||||
sevenseg UUD2 (.HEX0(HEX2), .SW(4'h0));
|
||||
sevenseg UUD3 (.HEX0(HEX3), .SW(4'h2));
|
||||
sevenseg UUD4 (.HEX0(HEX4), .SW(SW[3:0]));
|
||||
sevenseg UUD5 (.HEX0(HEX5), .SW(SW[7:4]));
|
||||
sevenseg UUD6 (.HEX0(HEX6), .SW(SW[11:8]));
|
||||
sevenseg UUD7 (.HEX0(HEX7), .SW(SW[15:12]));
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,6 @@
|
|||
Source of the files:
|
||||
http://www.asic-world.com/examples/verilog/lfsr.html
|
||||
|
||||
Run first: runme_presynth
|
||||
Generate output netlist with run_max10 or run_cycloneiv
|
||||
Then, check with: runme_postsynth
|
|
@ -0,0 +1,35 @@
|
|||
`default_nettype none
|
||||
module lfsr_updown (
|
||||
clk , // Clock input
|
||||
reset , // Reset input
|
||||
enable , // Enable input
|
||||
up_down , // Up Down input
|
||||
count , // Count output
|
||||
overflow // Overflow output
|
||||
);
|
||||
|
||||
input clk;
|
||||
input reset;
|
||||
input enable;
|
||||
input up_down;
|
||||
|
||||
output [7 : 0] count;
|
||||
output overflow;
|
||||
|
||||
reg [7 : 0] count;
|
||||
|
||||
assign overflow = (up_down) ? (count == {{7{1'b0}}, 1'b1}) :
|
||||
(count == {1'b1, {7{1'b0}}}) ;
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset)
|
||||
count <= {7{1'b0}};
|
||||
else if (enable) begin
|
||||
if (up_down) begin
|
||||
count <= {~(^(count & 8'b01100011)),count[7:1]};
|
||||
end else begin
|
||||
count <= {count[5:0],~(^(count & 8'b10110001))};
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,34 @@
|
|||
module tb();
|
||||
reg clk;
|
||||
reg reset;
|
||||
reg enable;
|
||||
reg up_down;
|
||||
|
||||
wire [7 : 0] count;
|
||||
wire overflow;
|
||||
|
||||
initial begin
|
||||
$monitor("rst %b en %b updown %b cnt %b overflow %b",
|
||||
reset,enable,up_down,count, overflow);
|
||||
clk = 0;
|
||||
reset = 1;
|
||||
enable = 0;
|
||||
up_down = 0;
|
||||
#10 reset = 0;
|
||||
#1 enable = 1;
|
||||
#20 up_down = 1;
|
||||
#30 $finish;
|
||||
end
|
||||
|
||||
always #1 clk = ~clk;
|
||||
|
||||
lfsr_updown U(
|
||||
.clk ( clk ),
|
||||
.reset ( reset ),
|
||||
.enable ( enable ),
|
||||
.up_down ( up_down ),
|
||||
.count ( count ),
|
||||
.overflow ( overflow )
|
||||
);
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/env bash
|
||||
yosys -p "synth_intel -family cycloneiv -top lfsr_updown -vqm top.vqm" lfsr_updown.v
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/env bash
|
||||
yosys -p "synth_intel -family max10 -top lfsr_updown -vqm top.vqm" lfsr_updown.v
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
iverilog -D POST_IMPL -o verif_post -s tb lfsr_updown_tb.v top.vqm $(yosys-config --datdir/altera_intel/max10/cells_comb_max10.v)
|
||||
vvp -N verif_post
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
iverilog -o presynth lfsr_updown_tb.v lfsr_updown.v &&\
|
||||
|
||||
vvp -N presynth
|
|
@ -0,0 +1,3 @@
|
|||
osu035_stdcells.lib
|
||||
example.yslog
|
||||
example.edif
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
example.edif: example.ys example.v example.constr osu035_stdcells.lib
|
||||
yosys -l example.yslog -q example.ys
|
||||
|
||||
osu035_stdcells.lib:
|
||||
rm -f osu035_stdcells.lib.part osu035_stdcells.lib
|
||||
wget -O osu035_stdcells.lib.part https://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/cadence/lib/ami035/signalstorm/osu035_stdcells.lib
|
||||
mv osu035_stdcells.lib.part osu035_stdcells.lib
|
||||
|
||||
clean:
|
||||
rm -f osu035_stdcells.lib
|
||||
rm -f example.yslog example.edif
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
set_driving_cell INVX1
|
||||
set_load 0.015
|
|
@ -0,0 +1,3 @@
|
|||
module top (input clk, input [7:0] a, b, output reg [15:0] c);
|
||||
always @(posedge clk) c <= a * b;
|
||||
endmodule
|
|
@ -0,0 +1,11 @@
|
|||
read_verilog example.v
|
||||
read_liberty -lib osu035_stdcells.lib
|
||||
|
||||
synth -top top
|
||||
|
||||
dfflibmap -liberty osu035_stdcells.lib
|
||||
abc -D 10000 -constr example.constr -liberty osu035_stdcells.lib
|
||||
opt_clean
|
||||
|
||||
stat -liberty osu035_stdcells.lib
|
||||
write_edif example.edif
|
|
@ -20,3 +20,5 @@ demo6.smt2
|
|||
demo6.yslog
|
||||
demo7.smt2
|
||||
demo7.yslog
|
||||
demo8.smt2
|
||||
demo8.yslog
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
all: demo1 demo2 demo3 demo4 demo5 demo6 demo7
|
||||
all: demo1 demo2 demo3 demo4 demo5 demo6 demo7 demo8
|
||||
|
||||
demo1: demo1.smt2
|
||||
yosys-smtbmc --dump-vcd demo1.vcd demo1.smt2
|
||||
|
@ -25,6 +25,9 @@ demo6: demo6.smt2
|
|||
demo7: demo7.smt2
|
||||
yosys-smtbmc -t 10 demo7.smt2
|
||||
|
||||
demo8: demo8.smt2
|
||||
yosys-smtbmc -s z3 -t 1 -g demo8.smt2
|
||||
|
||||
demo1.smt2: demo1.v
|
||||
yosys -ql demo1.yslog -p 'read_verilog -formal demo1.v; prep -top demo1 -nordff; write_smt2 -wires demo1.smt2'
|
||||
|
||||
|
@ -46,6 +49,9 @@ demo6.smt2: demo6.v
|
|||
demo7.smt2: demo7.v
|
||||
yosys -ql demo7.yslog -p 'read_verilog -formal demo7.v; prep -top demo7 -nordff; write_smt2 -wires demo7.smt2'
|
||||
|
||||
demo8.smt2: demo8.v
|
||||
yosys -ql demo8.yslog -p 'read_verilog -formal demo8.v; prep -top demo8 -nordff; write_smt2 -stbv -wires demo8.smt2'
|
||||
|
||||
clean:
|
||||
rm -f demo1.yslog demo1.smt2 demo1.vcd
|
||||
rm -f demo2.yslog demo2.smt2 demo2.vcd demo2.smtc demo2_tb.v demo2_tb demo2_tb.vcd
|
||||
|
@ -54,6 +60,7 @@ clean:
|
|||
rm -f demo5.yslog demo5.smt2 demo5.vcd
|
||||
rm -f demo6.yslog demo6.smt2
|
||||
rm -f demo7.yslog demo7.smt2
|
||||
rm -f demo8.yslog demo8.smt2
|
||||
|
||||
.PHONY: demo1 demo2 demo3 demo4 demo5 demo6 demo7 clean
|
||||
.PHONY: demo1 demo2 demo3 demo4 demo5 demo6 demo7 demo8 clean
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
module demo2(input clk, input [4:0] addr, output reg [31:0] data);
|
||||
reg [31:0] mem [0:31];
|
||||
always @(posedge clk)
|
||||
always @(negedge clk)
|
||||
data <= mem[addr];
|
||||
|
||||
reg [31:0] used_addr = 0;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
// Simple exists-forall demo
|
||||
|
||||
module demo8;
|
||||
wire [7:0] prime = $anyconst;
|
||||
wire [3:0] factor = $allconst;
|
||||
|
||||
always @* begin
|
||||
if (1 < factor && factor < prime)
|
||||
assume((prime % factor) != 0);
|
||||
assume(prime > 1);
|
||||
end
|
||||
endmodule
|
|
@ -2,6 +2,7 @@
|
|||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
* Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -35,16 +36,16 @@ YOSYS_NAMESPACE_BEGIN
|
|||
using namespace AST;
|
||||
using namespace AST_INTERNAL;
|
||||
|
||||
// instanciate global variables (public API)
|
||||
// instantiate global variables (public API)
|
||||
namespace AST {
|
||||
std::string current_filename;
|
||||
void (*set_line_num)(int) = NULL;
|
||||
int (*get_line_num)() = NULL;
|
||||
}
|
||||
|
||||
// instanciate global variables (private API)
|
||||
// instantiate global variables (private API)
|
||||
namespace AST_INTERNAL {
|
||||
bool flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
|
||||
bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
|
||||
bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire;
|
||||
AstNode *current_ast, *current_ast_mod;
|
||||
std::map<std::string, AstNode*> current_scope;
|
||||
|
@ -84,6 +85,9 @@ std::string AST::type2str(AstNodeType type)
|
|||
X(AST_PREFIX)
|
||||
X(AST_ASSERT)
|
||||
X(AST_ASSUME)
|
||||
X(AST_LIVE)
|
||||
X(AST_FAIR)
|
||||
X(AST_COVER)
|
||||
X(AST_FCALL)
|
||||
X(AST_TO_BITS)
|
||||
X(AST_TO_SIGNED)
|
||||
|
@ -168,8 +172,7 @@ bool AstNode::get_bool_attribute(RTLIL::IdString id)
|
|||
|
||||
AstNode *attr = attributes.at(id);
|
||||
if (attr->type != AST_CONSTANT)
|
||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
||||
id.c_str(), attr->filename.c_str(), attr->linenum);
|
||||
log_file_error(attr->filename, attr->linenum, "Attribute `%s' with non-constant value!\n", id.c_str());
|
||||
|
||||
return attr->integer != 0;
|
||||
}
|
||||
|
@ -188,8 +191,10 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
|
|||
is_input = false;
|
||||
is_output = false;
|
||||
is_reg = false;
|
||||
is_logic = false;
|
||||
is_signed = false;
|
||||
is_string = false;
|
||||
was_checked = false;
|
||||
range_valid = false;
|
||||
range_swapped = false;
|
||||
port_id = 0;
|
||||
|
@ -209,7 +214,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
|
|||
}
|
||||
|
||||
// create a (deep recursive) copy of a node
|
||||
AstNode *AstNode::clone()
|
||||
AstNode *AstNode::clone() const
|
||||
{
|
||||
AstNode *that = new AstNode;
|
||||
*that = *this;
|
||||
|
@ -221,7 +226,7 @@ AstNode *AstNode::clone()
|
|||
}
|
||||
|
||||
// create a (deep recursive) copy of a node use 'other' as target root node
|
||||
void AstNode::cloneInto(AstNode *other)
|
||||
void AstNode::cloneInto(AstNode *other) const
|
||||
{
|
||||
AstNode *tmp = clone();
|
||||
other->delete_children();
|
||||
|
@ -251,7 +256,7 @@ AstNode::~AstNode()
|
|||
|
||||
// create a nice text representation of the node
|
||||
// (traverse tree by recursion, use 'other' pointer for diffing two AST trees)
|
||||
void AstNode::dumpAst(FILE *f, std::string indent)
|
||||
void AstNode::dumpAst(FILE *f, std::string indent) const
|
||||
{
|
||||
if (f == NULL) {
|
||||
for (auto f : log_files)
|
||||
|
@ -262,10 +267,12 @@ void AstNode::dumpAst(FILE *f, std::string indent)
|
|||
std::string type_name = type2str(type);
|
||||
fprintf(f, "%s%s <%s:%d>", indent.c_str(), type_name.c_str(), filename.c_str(), linenum);
|
||||
|
||||
if (!flag_no_dump_ptr) {
|
||||
if (id2ast)
|
||||
fprintf(f, " [%p -> %p]", this, id2ast);
|
||||
else
|
||||
fprintf(f, " [%p]", this);
|
||||
}
|
||||
|
||||
if (!str.empty())
|
||||
fprintf(f, " str='%s'", str.c_str());
|
||||
|
@ -282,7 +289,9 @@ void AstNode::dumpAst(FILE *f, std::string indent)
|
|||
fprintf(f, " input");
|
||||
if (is_output)
|
||||
fprintf(f, " output");
|
||||
if (is_reg)
|
||||
if (is_logic)
|
||||
fprintf(f, " logic");
|
||||
if (is_reg) // this is an AST dump, not Verilog - if we see "logic reg" that's fine.
|
||||
fprintf(f, " reg");
|
||||
if (is_signed)
|
||||
fprintf(f, " signed");
|
||||
|
@ -330,7 +339,7 @@ static std::string id2vl(std::string txt)
|
|||
}
|
||||
|
||||
// dump AST node as Verilog pseudo-code
|
||||
void AstNode::dumpVlog(FILE *f, std::string indent)
|
||||
void AstNode::dumpVlog(FILE *f, std::string indent) const
|
||||
{
|
||||
bool first = true;
|
||||
std::string txt;
|
||||
|
@ -649,6 +658,8 @@ bool AstNode::operator==(const AstNode &other) const
|
|||
return false;
|
||||
if (is_output != other.is_output)
|
||||
return false;
|
||||
if (is_logic != other.is_logic)
|
||||
return false;
|
||||
if (is_reg != other.is_reg)
|
||||
return false;
|
||||
if (is_signed != other.is_signed)
|
||||
|
@ -752,7 +763,7 @@ AstNode *AstNode::mkconst_str(const std::string &str)
|
|||
return node;
|
||||
}
|
||||
|
||||
bool AstNode::bits_only_01()
|
||||
bool AstNode::bits_only_01() const
|
||||
{
|
||||
for (auto bit : bits)
|
||||
if (bit != RTLIL::S0 && bit != RTLIL::S1)
|
||||
|
@ -803,7 +814,7 @@ RTLIL::Const AstNode::asParaConst()
|
|||
return val;
|
||||
}
|
||||
|
||||
bool AstNode::asBool()
|
||||
bool AstNode::asBool() const
|
||||
{
|
||||
log_assert(type == AST_CONSTANT);
|
||||
for (auto &bit : bits)
|
||||
|
@ -812,7 +823,7 @@ bool AstNode::asBool()
|
|||
return false;
|
||||
}
|
||||
|
||||
int AstNode::isConst()
|
||||
int AstNode::isConst() const
|
||||
{
|
||||
if (type == AST_CONSTANT)
|
||||
return 1;
|
||||
|
@ -892,9 +903,9 @@ RTLIL::Const AstNode::realAsConst(int width)
|
|||
}
|
||||
|
||||
// create a new AstModule from an AST_MODULE AST node
|
||||
static AstModule* process_module(AstNode *ast, bool defer)
|
||||
static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast = NULL)
|
||||
{
|
||||
log_assert(ast->type == AST_MODULE);
|
||||
log_assert(ast->type == AST_MODULE || ast->type == AST_INTERFACE);
|
||||
|
||||
if (defer)
|
||||
log("Storing AST representation for module `%s'.\n", ast->str.c_str());
|
||||
|
@ -905,9 +916,14 @@ static AstModule* process_module(AstNode *ast, bool defer)
|
|||
current_module->ast = NULL;
|
||||
current_module->name = ast->str;
|
||||
current_module->attributes["\\src"] = stringf("%s:%d", ast->filename.c_str(), ast->linenum);
|
||||
current_module->set_bool_attribute("\\cells_not_processed");
|
||||
|
||||
current_ast_mod = ast;
|
||||
AstNode *ast_before_simplify = ast->clone();
|
||||
AstNode *ast_before_simplify;
|
||||
if (original_ast != NULL)
|
||||
ast_before_simplify = original_ast;
|
||||
else
|
||||
ast_before_simplify = ast->clone();
|
||||
|
||||
if (flag_dump_ast1) {
|
||||
log("Dumping Verilog AST before simplification:\n");
|
||||
|
@ -952,8 +968,7 @@ static AstModule* process_module(AstNode *ast, bool defer)
|
|||
|
||||
for (auto &attr : ast->attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
||||
attr.first.c_str(), ast->filename.c_str(), ast->linenum);
|
||||
log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||
current_module->attributes[attr.first] = attr.second->asAttrConst();
|
||||
}
|
||||
for (size_t i = 0; i < ast->children.size(); i++) {
|
||||
|
@ -978,6 +993,8 @@ static AstModule* process_module(AstNode *ast, bool defer)
|
|||
ignoreThisSignalsInInitial = RTLIL::SigSpec();
|
||||
}
|
||||
|
||||
if (ast->type == AST_INTERFACE)
|
||||
current_module->set_bool_attribute("\\is_interface");
|
||||
current_module->ast = ast_before_simplify;
|
||||
current_module->nolatches = flag_nolatches;
|
||||
current_module->nomeminit = flag_nomeminit;
|
||||
|
@ -999,12 +1016,13 @@ 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 dump_rtlil,
|
||||
bool nolatches, bool nomeminit, 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 no_dump_ptr, bool dump_vlog, bool dump_rtlil,
|
||||
bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire)
|
||||
{
|
||||
current_ast = ast;
|
||||
flag_dump_ast1 = dump_ast1;
|
||||
flag_dump_ast2 = dump_ast2;
|
||||
flag_no_dump_ptr = no_dump_ptr;
|
||||
flag_dump_vlog = dump_vlog;
|
||||
flag_dump_rtlil = dump_rtlil;
|
||||
flag_nolatches = nolatches;
|
||||
|
@ -1016,14 +1034,12 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
|
|||
flag_icells = icells;
|
||||
flag_autowire = autowire;
|
||||
|
||||
std::vector<AstNode*> global_decls;
|
||||
|
||||
log_assert(current_ast->type == AST_DESIGN);
|
||||
for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++)
|
||||
{
|
||||
if ((*it)->type == AST_MODULE)
|
||||
if ((*it)->type == AST_MODULE || (*it)->type == AST_INTERFACE)
|
||||
{
|
||||
for (auto n : global_decls)
|
||||
for (auto n : design->verilog_globals)
|
||||
(*it)->children.push_back(n->clone());
|
||||
|
||||
for (auto n : design->verilog_packages){
|
||||
|
@ -1041,12 +1057,19 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
|
|||
(*it)->str = "$abstract" + (*it)->str;
|
||||
|
||||
if (design->has((*it)->str)) {
|
||||
if (!ignore_redef)
|
||||
log_error("Re-definition of module `%s' at %s:%d!\n",
|
||||
(*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum);
|
||||
log("Ignoring re-definition of module `%s' at %s:%d!\n",
|
||||
RTLIL::Module *existing_mod = design->module((*it)->str);
|
||||
if (!nooverwrite && !overwrite && !existing_mod->get_bool_attribute("\\blackbox")) {
|
||||
log_file_error((*it)->filename, (*it)->linenum, "Re-definition of module `%s'!\n", (*it)->str.c_str());
|
||||
} else if (nooverwrite) {
|
||||
log("Ignoring re-definition of module `%s' at %s:%d.\n",
|
||||
(*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum);
|
||||
continue;
|
||||
} else {
|
||||
log("Replacing existing%s module `%s' at %s:%d.\n",
|
||||
existing_mod->get_bool_attribute("\\blackbox") ? " blackbox" : "",
|
||||
(*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum);
|
||||
design->remove(existing_mod);
|
||||
}
|
||||
}
|
||||
|
||||
design->add(process_module(*it, defer));
|
||||
|
@ -1054,7 +1077,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
|
|||
else if ((*it)->type == AST_PACKAGE)
|
||||
design->verilog_packages.push_back((*it)->clone());
|
||||
else
|
||||
global_decls.push_back(*it);
|
||||
design->verilog_globals.push_back((*it)->clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1065,8 +1088,264 @@ AstModule::~AstModule()
|
|||
delete ast;
|
||||
}
|
||||
|
||||
|
||||
// An interface port with modport is specified like this:
|
||||
// <interface_name>.<modport_name>
|
||||
// This function splits the interface_name from the modport_name, and fails if it is not a valid combination
|
||||
std::pair<std::string,std::string> AST::split_modport_from_type(std::string name_type)
|
||||
{
|
||||
std::string interface_type = "";
|
||||
std::string interface_modport = "";
|
||||
size_t ndots = std::count(name_type.begin(), name_type.end(), '.');
|
||||
// Separate the interface instance name from any modports:
|
||||
if (ndots == 0) { // Does not have modport
|
||||
interface_type = name_type;
|
||||
}
|
||||
else {
|
||||
std::stringstream name_type_stream(name_type);
|
||||
std::string segment;
|
||||
std::vector<std::string> seglist;
|
||||
while(std::getline(name_type_stream, segment, '.')) {
|
||||
seglist.push_back(segment);
|
||||
}
|
||||
if (ndots == 1) { // Has modport
|
||||
interface_type = seglist[0];
|
||||
interface_modport = seglist[1];
|
||||
}
|
||||
else { // Erroneous port type
|
||||
log_error("More than two '.' in signal port type (%s)\n", name_type.c_str());
|
||||
}
|
||||
}
|
||||
return std::pair<std::string,std::string>(interface_type, interface_modport);
|
||||
|
||||
}
|
||||
|
||||
AstNode * AST::find_modport(AstNode *intf, std::string name)
|
||||
{
|
||||
for (auto &ch : intf->children)
|
||||
if (ch->type == AST_MODPORT)
|
||||
if (ch->str == name) // Modport found
|
||||
return ch;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Iterate over all wires in an interface and add them as wires in the AST module:
|
||||
void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule, std::string intfname, AstNode *modport)
|
||||
{
|
||||
for (auto &wire_it : intfmodule->wires_){
|
||||
AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true)));
|
||||
std::string origname = log_id(wire_it.first);
|
||||
std::string newname = intfname + "." + origname;
|
||||
wire->str = newname;
|
||||
if (modport != NULL) {
|
||||
bool found_in_modport = false;
|
||||
// Search for the current wire in the wire list for the current modport
|
||||
for (auto &ch : modport->children) {
|
||||
if (ch->type == AST_MODPORTMEMBER) {
|
||||
std::string compare_name = "\\" + origname;
|
||||
if (ch->str == compare_name) { // Found signal. The modport decides whether it is input or output
|
||||
found_in_modport = true;
|
||||
wire->is_input = ch->is_input;
|
||||
wire->is_output = ch->is_output;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found_in_modport) {
|
||||
module_ast->children.push_back(wire);
|
||||
}
|
||||
else { // If not found in modport, do not create port
|
||||
delete wire;
|
||||
}
|
||||
}
|
||||
else { // If no modport, set inout
|
||||
wire->is_input = true;
|
||||
wire->is_output = true;
|
||||
module_ast->children.push_back(wire);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When an interface instance is found in a module, the whole RTLIL for the module will be rederived again
|
||||
// from AST. The interface members are copied into the AST module with the prefix of the interface.
|
||||
void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module*> local_interfaces)
|
||||
{
|
||||
bool is_top = false;
|
||||
AstNode *new_ast = ast->clone();
|
||||
for (auto &intf : local_interfaces) {
|
||||
std::string intfname = intf.first.str();
|
||||
RTLIL::Module *intfmodule = intf.second;
|
||||
for (auto &wire_it : intfmodule->wires_){
|
||||
AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true)));
|
||||
std::string newname = log_id(wire_it.first);
|
||||
newname = intfname + "." + newname;
|
||||
wire->str = newname;
|
||||
new_ast->children.push_back(wire);
|
||||
}
|
||||
}
|
||||
|
||||
AstNode *ast_before_replacing_interface_ports = new_ast->clone();
|
||||
|
||||
// Explode all interface ports. Note this will only have an effect on 'top
|
||||
// level' modules. Other sub-modules will have their interface ports
|
||||
// exploded via the derive(..) function
|
||||
for (size_t i =0; i<new_ast->children.size(); i++)
|
||||
{
|
||||
AstNode *ch2 = new_ast->children[i];
|
||||
if (ch2->type == AST_INTERFACEPORT) { // Is an interface port
|
||||
std::string name_port = ch2->str; // Name of the interface port
|
||||
if (ch2->children.size() > 0) {
|
||||
for(size_t j=0; j<ch2->children.size();j++) {
|
||||
AstNode *ch = ch2->children[j];
|
||||
if(ch->type == AST_INTERFACEPORTTYPE) { // Found the AST node containing the type of the interface
|
||||
std::pair<std::string,std::string> res = split_modport_from_type(ch->str);
|
||||
std::string interface_type = res.first;
|
||||
std::string interface_modport = res.second; // Is "", if no modport
|
||||
if (design->modules_.count(interface_type) > 0) {
|
||||
// Add a cell to the module corresponding to the interface port such that
|
||||
// it can further propagated down if needed:
|
||||
AstNode *celltype_for_intf = new AstNode(AST_CELLTYPE);
|
||||
celltype_for_intf->str = interface_type;
|
||||
AstNode *cell_for_intf = new AstNode(AST_CELL, celltype_for_intf);
|
||||
cell_for_intf->str = name_port + "_inst_from_top_dummy";
|
||||
new_ast->children.push_back(cell_for_intf);
|
||||
|
||||
// Get all members of this non-overridden dummy interface instance:
|
||||
RTLIL::Module *intfmodule = design->modules_[interface_type]; // All interfaces should at this point in time (assuming
|
||||
// reprocess_module is called from the hierarchy pass) be
|
||||
// present in design->modules_
|
||||
AstModule *ast_module_of_interface = (AstModule*)intfmodule;
|
||||
std::string interface_modport_compare_str = "\\" + interface_modport;
|
||||
AstNode *modport = find_modport(ast_module_of_interface->ast, interface_modport_compare_str); // modport == NULL if no modport
|
||||
// Iterate over all wires in the interface and add them to the module:
|
||||
explode_interface_port(new_ast, intfmodule, name_port, modport);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The old module will be deleted. Rename and mark for deletion:
|
||||
std::string original_name = this->name.str();
|
||||
std::string changed_name = original_name + "_before_replacing_local_interfaces";
|
||||
design->rename(this, changed_name);
|
||||
this->set_bool_attribute("\\to_delete");
|
||||
|
||||
// Check if the module was the top module. If it was, we need to remove the top attribute and put it on the
|
||||
// new module.
|
||||
if (this->get_bool_attribute("\\initial_top")) {
|
||||
this->attributes.erase("\\initial_top");
|
||||
is_top = true;
|
||||
}
|
||||
|
||||
// Generate RTLIL from AST for the new module and add to the design:
|
||||
AstModule *newmod = process_module(new_ast, false, ast_before_replacing_interface_ports);
|
||||
delete(new_ast);
|
||||
design->add(newmod);
|
||||
RTLIL::Module* mod = design->module(original_name);
|
||||
if (is_top)
|
||||
mod->set_bool_attribute("\\top");
|
||||
|
||||
// Set the attribute "interfaces_replaced_in_module" so that it does not happen again.
|
||||
mod->set_bool_attribute("\\interfaces_replaced_in_module");
|
||||
}
|
||||
|
||||
// create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces
|
||||
// This method is used to explode the interface when the interface is a port of the module (not instantiated inside)
|
||||
RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail)
|
||||
{
|
||||
AstNode *new_ast = NULL;
|
||||
std::string modname = derive_common(design, parameters, &new_ast, mayfail);
|
||||
|
||||
// Since interfaces themselves may be instantiated with different parameters,
|
||||
// "modname" must also take those into account, so that unique modules
|
||||
// are derived for any variant of interface connections:
|
||||
std::string interf_info = "";
|
||||
|
||||
bool has_interfaces = false;
|
||||
for(auto &intf : interfaces) {
|
||||
interf_info += log_id(intf.second->name);
|
||||
has_interfaces = true;
|
||||
}
|
||||
|
||||
if (has_interfaces)
|
||||
modname += "$interfaces$" + interf_info;
|
||||
|
||||
|
||||
if (!design->has(modname)) {
|
||||
new_ast->str = modname;
|
||||
|
||||
// Iterate over all interfaces which are ports in this module:
|
||||
for(auto &intf : interfaces) {
|
||||
RTLIL::Module * intfmodule = intf.second;
|
||||
std::string intfname = intf.first.str();
|
||||
// Check if a modport applies for the interface port:
|
||||
AstNode *modport = NULL;
|
||||
if (modports.count(intfname) > 0) {
|
||||
std::string interface_modport = modports.at(intfname).str();
|
||||
AstModule *ast_module_of_interface = (AstModule*)intfmodule;
|
||||
AstNode *ast_node_of_interface = ast_module_of_interface->ast;
|
||||
modport = find_modport(ast_node_of_interface, interface_modport);
|
||||
}
|
||||
// Iterate over all wires in the interface and add them to the module:
|
||||
explode_interface_port(new_ast, intfmodule, intfname, modport);
|
||||
}
|
||||
|
||||
design->add(process_module(new_ast, false));
|
||||
design->module(modname)->check();
|
||||
|
||||
RTLIL::Module* mod = design->module(modname);
|
||||
|
||||
// Now that the interfaces have been exploded, we can delete the dummy port related to every interface.
|
||||
for(auto &intf : interfaces) {
|
||||
if(mod->wires_.count(intf.first)) {
|
||||
mod->wires_.erase(intf.first);
|
||||
mod->fixup_ports();
|
||||
// We copy the cell of the interface to the sub-module such that it can further be found if it is propagated
|
||||
// down to sub-sub-modules etc.
|
||||
RTLIL::Cell * new_subcell = mod->addCell(intf.first, intf.second->name);
|
||||
new_subcell->set_bool_attribute("\\is_interface");
|
||||
}
|
||||
else {
|
||||
log_error("No port with matching name found (%s) in %s. Stopping\n", log_id(intf.first), modname.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// If any interfaces were replaced, set the attribute 'interfaces_replaced_in_module':
|
||||
if (interfaces.size() > 0) {
|
||||
mod->set_bool_attribute("\\interfaces_replaced_in_module");
|
||||
}
|
||||
|
||||
} else {
|
||||
log("Found cached RTLIL representation for module `%s'.\n", modname.c_str());
|
||||
}
|
||||
|
||||
delete new_ast;
|
||||
return modname;
|
||||
}
|
||||
|
||||
// create a new parametric module (when needed) and return the name of the generated module - without support for interfaces
|
||||
RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail)
|
||||
{
|
||||
AstNode *new_ast = NULL;
|
||||
std::string modname = derive_common(design, parameters, &new_ast, mayfail);
|
||||
|
||||
if (!design->has(modname)) {
|
||||
new_ast->str = modname;
|
||||
design->add(process_module(new_ast, false));
|
||||
design->module(modname)->check();
|
||||
} else {
|
||||
log("Found cached RTLIL representation for module `%s'.\n", modname.c_str());
|
||||
}
|
||||
|
||||
delete new_ast;
|
||||
return modname;
|
||||
}
|
||||
|
||||
// create a new parametric module (when needed) and return the name of the generated module
|
||||
RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters)
|
||||
std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool)
|
||||
{
|
||||
std::string stripped_name = name.str();
|
||||
|
||||
|
@ -1105,6 +1384,9 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
|
|||
rewrite_parameter:
|
||||
para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
|
||||
delete child->children.at(0);
|
||||
if ((parameters[para_id].flags & RTLIL::CONST_FLAG_STRING) != 0)
|
||||
child->children[0] = AstNode::mkconst_str(parameters[para_id].decode_string());
|
||||
else
|
||||
child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0);
|
||||
parameters.erase(para_id);
|
||||
continue;
|
||||
|
@ -1115,8 +1397,16 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
|
|||
goto rewrite_parameter;
|
||||
}
|
||||
}
|
||||
if (parameters.size() > 0)
|
||||
log_error("Requested parameter `%s' does not exist in module `%s'!\n", parameters.begin()->first.c_str(), stripped_name.c_str());
|
||||
|
||||
for (auto param : parameters) {
|
||||
AstNode *defparam = new AstNode(AST_DEFPARAM, new AstNode(AST_IDENTIFIER));
|
||||
defparam->children[0]->str = param.first.str();
|
||||
if ((param.second.flags & RTLIL::CONST_FLAG_STRING) != 0)
|
||||
defparam->children.push_back(AstNode::mkconst_str(param.second.decode_string()));
|
||||
else
|
||||
defparam->children.push_back(AstNode::mkconst_bits(param.second.bits, (param.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0));
|
||||
new_ast->children.push_back(defparam);
|
||||
}
|
||||
|
||||
std::string modname;
|
||||
|
||||
|
@ -1127,15 +1417,8 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
|
|||
else
|
||||
modname = "$paramod" + stripped_name + para_info;
|
||||
|
||||
if (!design->has(modname)) {
|
||||
new_ast->str = modname;
|
||||
design->add(process_module(new_ast, false));
|
||||
design->module(modname)->check();
|
||||
} else {
|
||||
log("Found cached RTLIL representation for module `%s'.\n", modname.c_str());
|
||||
}
|
||||
|
||||
delete new_ast;
|
||||
(*new_ast_out) = new_ast;
|
||||
return modname;
|
||||
}
|
||||
|
||||
|
@ -1177,4 +1460,3 @@ void AST::use_internal_line_num()
|
|||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/* -*- c++ -*-
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||
|
@ -65,6 +65,9 @@ namespace AST
|
|||
AST_PREFIX,
|
||||
AST_ASSERT,
|
||||
AST_ASSUME,
|
||||
AST_LIVE,
|
||||
AST_FAIR,
|
||||
AST_COVER,
|
||||
|
||||
AST_FCALL,
|
||||
AST_TO_BITS,
|
||||
|
@ -139,6 +142,11 @@ namespace AST
|
|||
AST_NEGEDGE,
|
||||
AST_EDGE,
|
||||
|
||||
AST_INTERFACE,
|
||||
AST_INTERFACEPORT,
|
||||
AST_INTERFACEPORTTYPE,
|
||||
AST_MODPORT,
|
||||
AST_MODPORTMEMBER,
|
||||
AST_PACKAGE
|
||||
};
|
||||
|
||||
|
@ -165,7 +173,7 @@ namespace AST
|
|||
// node content - most of it is unused in most node types
|
||||
std::string str;
|
||||
std::vector<RTLIL::State> bits;
|
||||
bool is_input, is_output, is_reg, is_signed, is_string, range_valid, range_swapped;
|
||||
bool is_input, is_output, is_reg, is_logic, is_signed, is_string, range_valid, range_swapped, was_checked;
|
||||
int port_id, range_left, range_right;
|
||||
uint32_t integer;
|
||||
double realvalue;
|
||||
|
@ -187,8 +195,8 @@ namespace AST
|
|||
|
||||
// creating and deleting nodes
|
||||
AstNode(AstNodeType type = AST_NONE, AstNode *child1 = NULL, AstNode *child2 = NULL, AstNode *child3 = NULL);
|
||||
AstNode *clone();
|
||||
void cloneInto(AstNode *other);
|
||||
AstNode *clone() const;
|
||||
void cloneInto(AstNode *other) const;
|
||||
void delete_children();
|
||||
~AstNode();
|
||||
|
||||
|
@ -231,8 +239,8 @@ namespace AST
|
|||
AstNode *eval_const_function(AstNode *fcall);
|
||||
|
||||
// create a human-readable text representation of the AST (for debugging)
|
||||
void dumpAst(FILE *f, std::string indent);
|
||||
void dumpVlog(FILE *f, std::string indent);
|
||||
void dumpAst(FILE *f, std::string indent) const;
|
||||
void dumpVlog(FILE *f, std::string indent) const;
|
||||
|
||||
// used by genRTLIL() for detecting expression width and sign
|
||||
void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL);
|
||||
|
@ -261,27 +269,30 @@ namespace AST
|
|||
RTLIL::Const asAttrConst();
|
||||
RTLIL::Const asParaConst();
|
||||
uint64_t asInt(bool is_signed);
|
||||
bool bits_only_01();
|
||||
bool asBool();
|
||||
bool bits_only_01() const;
|
||||
bool asBool() const;
|
||||
|
||||
// helper functions for real valued const eval
|
||||
int isConst(); // return '1' for AST_CONSTANT and '2' for AST_REALVALUE
|
||||
int isConst() const; // return '1' for AST_CONSTANT and '2' for AST_REALVALUE
|
||||
double asReal(bool is_signed);
|
||||
RTLIL::Const realAsConst(int width);
|
||||
};
|
||||
|
||||
// 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 dump_rtlil, bool nolatches, bool nomeminit,
|
||||
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 no_dump_ptr, bool dump_vlog, bool dump_rtlil, bool nolatches, bool nomeminit,
|
||||
bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire);
|
||||
|
||||
// parametric modules are supported directly by the AST library
|
||||
// therefore we need our own derivate of RTLIL::Module with overloaded virtual functions
|
||||
struct AstModule : RTLIL::Module {
|
||||
AstNode *ast;
|
||||
bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire;
|
||||
virtual ~AstModule();
|
||||
virtual RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters);
|
||||
virtual RTLIL::Module *clone() const;
|
||||
~AstModule() YS_OVERRIDE;
|
||||
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE;
|
||||
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE;
|
||||
std::string derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool mayfail);
|
||||
void reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module *> local_interfaces) YS_OVERRIDE;
|
||||
RTLIL::Module *clone() const YS_OVERRIDE;
|
||||
};
|
||||
|
||||
// this must be set by the language frontend before parsing the sources
|
||||
|
@ -297,12 +308,17 @@ namespace AST
|
|||
|
||||
// call a DPI function
|
||||
AstNode *dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args);
|
||||
|
||||
// Helper functions related to handling SystemVerilog interfaces
|
||||
std::pair<std::string,std::string> split_modport_from_type(std::string name_type);
|
||||
AstNode * find_modport(AstNode *intf, std::string name);
|
||||
void explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule, std::string intfname, AstNode *modport);
|
||||
}
|
||||
|
||||
namespace AST_INTERNAL
|
||||
{
|
||||
// internal state variables
|
||||
extern bool flag_dump_ast1, flag_dump_ast2, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
|
||||
extern bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
|
||||
extern bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire;
|
||||
extern AST::AstNode *current_ast, *current_ast_mod;
|
||||
extern std::map<std::string, AST::AstNode*> current_scope;
|
||||
|
|
|
@ -55,8 +55,7 @@ static RTLIL::SigSpec uniop2rtlil(AstNode *that, std::string type, int result_wi
|
|||
if (gen_attributes)
|
||||
for (auto &attr : that->attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
||||
attr.first.c_str(), that->filename.c_str(), that->linenum);
|
||||
log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
||||
}
|
||||
|
||||
|
@ -89,8 +88,7 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s
|
|||
if (that != NULL)
|
||||
for (auto &attr : that->attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
||||
attr.first.c_str(), that->filename.c_str(), that->linenum);
|
||||
log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
||||
}
|
||||
|
||||
|
@ -117,8 +115,7 @@ static RTLIL::SigSpec binop2rtlil(AstNode *that, std::string type, int result_wi
|
|||
|
||||
for (auto &attr : that->attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
||||
attr.first.c_str(), that->filename.c_str(), that->linenum);
|
||||
log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
||||
}
|
||||
|
||||
|
@ -152,8 +149,7 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const
|
|||
|
||||
for (auto &attr : that->attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
||||
attr.first.c_str(), that->filename.c_str(), that->linenum);
|
||||
log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
||||
}
|
||||
|
||||
|
@ -207,8 +203,8 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->linenum, autoidx++);
|
||||
for (auto &attr : always->attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
||||
attr.first.c_str(), always->filename.c_str(), always->linenum);
|
||||
log_file_error(always->filename, always->linenum, "Attribute `%s' with non-constant value!\n",
|
||||
attr.first.c_str());
|
||||
proc->attributes[attr.first] = attr.second->asAttrConst();
|
||||
}
|
||||
current_module->processes[proc->name] = proc;
|
||||
|
@ -223,16 +219,22 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
bool found_global_syncs = false;
|
||||
bool found_anyedge_syncs = false;
|
||||
for (auto child : always->children)
|
||||
{
|
||||
if ((child->type == AST_POSEDGE || child->type == AST_NEGEDGE) && GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER &&
|
||||
child->children.at(0)->id2ast && child->children.at(0)->id2ast->type == AST_WIRE && child->children.at(0)->id2ast->get_bool_attribute("\\gclk")) {
|
||||
found_global_syncs = true;
|
||||
}
|
||||
if (child->type == AST_EDGE) {
|
||||
if (GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->str == "\\$global_clock")
|
||||
found_global_syncs = true;
|
||||
else
|
||||
found_anyedge_syncs = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_anyedge_syncs) {
|
||||
if (found_global_syncs)
|
||||
log_error("Found non-synthesizable event list at %s:%d!\n", always->filename.c_str(), always->linenum);
|
||||
log_file_error(always->filename, always->linenum, "Found non-synthesizable event list!\n");
|
||||
log("Note: Assuming pure combinatorial block at %s:%d in\n", always->filename.c_str(), always->linenum);
|
||||
log("compliance with IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002. Recommending\n");
|
||||
log("use of @* instead of @(...) for better match of synthesis and simulation.\n");
|
||||
|
@ -242,14 +244,17 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
bool found_clocked_sync = false;
|
||||
for (auto child : always->children)
|
||||
if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) {
|
||||
if (GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->id2ast &&
|
||||
child->children.at(0)->id2ast->type == AST_WIRE && child->children.at(0)->id2ast->get_bool_attribute("\\gclk"))
|
||||
continue;
|
||||
found_clocked_sync = true;
|
||||
if (found_global_syncs || found_anyedge_syncs)
|
||||
log_error("Found non-synthesizable event list at %s:%d!\n", always->filename.c_str(), always->linenum);
|
||||
log_file_error(always->filename, always->linenum, "Found non-synthesizable event list!\n");
|
||||
RTLIL::SyncRule *syncrule = new RTLIL::SyncRule;
|
||||
syncrule->type = child->type == AST_POSEDGE ? RTLIL::STp : RTLIL::STn;
|
||||
syncrule->signal = child->children[0]->genRTLIL();
|
||||
if (GetSize(syncrule->signal) != 1)
|
||||
log_error("Found posedge/negedge event on a signal that is not 1 bit wide at %s:%d!\n", always->filename.c_str(), always->linenum);
|
||||
log_file_error(always->filename, always->linenum, "Found posedge/negedge event on a signal that is not 1 bit wide!\n");
|
||||
addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, true);
|
||||
proc->syncs.push_back(syncrule);
|
||||
}
|
||||
|
@ -471,8 +476,7 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
|
||||
for (auto &attr : ast->attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
||||
attr.first.c_str(), ast->filename.c_str(), ast->linenum);
|
||||
log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||
sw->attributes[attr.first] = attr.second->asAttrConst();
|
||||
}
|
||||
|
||||
|
@ -540,12 +544,12 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
break;
|
||||
|
||||
case AST_WIRE:
|
||||
log_error("Found wire declaration in block without label at at %s:%d!\n", ast->filename.c_str(), ast->linenum);
|
||||
log_file_error(ast->filename, ast->linenum, "Found wire declaration in block without label!\n");
|
||||
break;
|
||||
|
||||
case AST_PARAMETER:
|
||||
case AST_LOCALPARAM:
|
||||
log_error("Found parameter declaration in block without label at at %s:%d!\n", ast->filename.c_str(), ast->linenum);
|
||||
log_file_error(ast->filename, ast->linenum, "Found parameter declaration in block without label!\n");
|
||||
break;
|
||||
|
||||
case AST_NONE:
|
||||
|
@ -554,6 +558,8 @@ struct AST_INTERNAL::ProcessGenerator
|
|||
break;
|
||||
|
||||
default:
|
||||
// ast->dumpAst(NULL, "ast> ");
|
||||
// current_ast_mod->dumpAst(NULL, "mod> ");
|
||||
log_abort();
|
||||
}
|
||||
}
|
||||
|
@ -591,7 +597,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
if (id_ast == NULL && current_scope.count(str))
|
||||
id_ast = current_scope.at(str);
|
||||
if (!id_ast)
|
||||
log_error("Failed to resolve identifier %s for width detection at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Failed to resolve identifier %s for width detection!\n", str.c_str());
|
||||
if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM) {
|
||||
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;
|
||||
|
@ -601,7 +607,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
if (id_ast->children[0]->type == AST_CONSTANT)
|
||||
this_width = id_ast->children[0]->bits.size();
|
||||
else
|
||||
log_error("Failed to detect width for parameter %s at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Failed to detect width for parameter %s!\n", str.c_str());
|
||||
if (children.size() != 0)
|
||||
range = children[0];
|
||||
} else if (id_ast->type == AST_WIRE || id_ast->type == AST_AUTOWIRE) {
|
||||
|
@ -613,7 +619,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
// log("---\n");
|
||||
// id_ast->dumpAst(NULL, "decl> ");
|
||||
// dumpAst(NULL, "ref> ");
|
||||
log_error("Failed to detect width of signal access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Failed to detect width of signal access `%s'!\n", str.c_str());
|
||||
}
|
||||
} else {
|
||||
this_width = id_ast->range_left - id_ast->range_right + 1;
|
||||
|
@ -624,10 +630,10 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
this_width = 32;
|
||||
} else if (id_ast->type == AST_MEMORY) {
|
||||
if (!id_ast->children[0]->range_valid)
|
||||
log_error("Failed to detect width of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", str.c_str());
|
||||
this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1;
|
||||
} else
|
||||
log_error("Failed to detect width for identifier %s at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Failed to detect width for identifier %s!\n", str.c_str());
|
||||
if (range) {
|
||||
if (range->children.size() == 1)
|
||||
this_width = 1;
|
||||
|
@ -637,8 +643,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
|
||||
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
|
||||
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);
|
||||
log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
|
||||
this_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
|
||||
delete left_at_zero_ast;
|
||||
delete right_at_zero_ast;
|
||||
|
@ -654,7 +659,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
case AST_TO_BITS:
|
||||
while (children[0]->simplify(true, false, false, 1, -1, false, false) == true) { }
|
||||
if (children[0]->type != AST_CONSTANT)
|
||||
log_error("Left operand of tobits expression is not constant at %s:%d!\n", filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Left operand of tobits expression is not constant!\n");
|
||||
children[1]->detectSignWidthWorker(sub_width_hint, sign_hint);
|
||||
width_hint = max(width_hint, children[0]->bitsAsConst().as_int());
|
||||
break;
|
||||
|
@ -682,7 +687,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
case AST_REPLICATE:
|
||||
while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { }
|
||||
if (children[0]->type != AST_CONSTANT)
|
||||
log_error("Left operand of replicate expression is not constant at %s:%d!\n", filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Left operand of replicate expression is not constant!\n");
|
||||
children[1]->detectSignWidthWorker(sub_width_hint, sub_sign_hint);
|
||||
width_hint = max(width_hint, children[0]->bitsAsConst().as_int() * sub_width_hint);
|
||||
sign_hint = false;
|
||||
|
@ -756,18 +761,18 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
if (!id2ast->is_signed)
|
||||
sign_hint = false;
|
||||
if (!id2ast->children[0]->range_valid)
|
||||
log_error("Failed to detect width of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", str.c_str());
|
||||
this_width = id2ast->children[0]->range_left - id2ast->children[0]->range_right + 1;
|
||||
width_hint = max(width_hint, this_width);
|
||||
break;
|
||||
|
||||
case AST_FCALL:
|
||||
if (str == "\\$anyconst" || str == "\\$anyseq") {
|
||||
if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") {
|
||||
if (GetSize(children) == 1) {
|
||||
while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { }
|
||||
if (children[0]->type != AST_CONSTANT)
|
||||
log_error("System function %s called with non-const argument at %s:%d!\n",
|
||||
RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "System function %s called with non-const argument!\n",
|
||||
RTLIL::unescape_id(str).c_str());
|
||||
width_hint = max(width_hint, int(children[0]->asInt(true)));
|
||||
}
|
||||
break;
|
||||
|
@ -788,8 +793,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
|||
default:
|
||||
for (auto f : log_files)
|
||||
current_ast->dumpAst(f, "verilog-ast> ");
|
||||
log_error("Don't know how to detect sign and width for %s node at %s:%d!\n",
|
||||
type2str(type).c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Don't know how to detect sign and width for %s node!\n", type2str(type).c_str());
|
||||
}
|
||||
|
||||
if (*found_real)
|
||||
|
@ -842,6 +846,35 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
case AST_GENIF:
|
||||
case AST_GENCASE:
|
||||
case AST_PACKAGE:
|
||||
case AST_MODPORT:
|
||||
case AST_MODPORTMEMBER:
|
||||
break;
|
||||
case AST_INTERFACEPORT: {
|
||||
// If a port in a module with unknown type is found, mark it with the attribute 'is_interface'
|
||||
// This is used by the hierarchy pass to know when it can replace interface connection with the individual
|
||||
// signals.
|
||||
RTLIL::Wire *wire = current_module->addWire(str, 1);
|
||||
wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
|
||||
wire->start_offset = 0;
|
||||
wire->port_id = port_id;
|
||||
wire->port_input = true;
|
||||
wire->port_output = true;
|
||||
wire->set_bool_attribute("\\is_interface");
|
||||
if (children.size() > 0) {
|
||||
for(size_t i=0; i<children.size();i++) {
|
||||
if(children[i]->type == AST_INTERFACEPORTTYPE) {
|
||||
std::pair<std::string,std::string> res = AST::split_modport_from_type(children[i]->str);
|
||||
wire->attributes["\\interface_type"] = res.first;
|
||||
if (res.second != "")
|
||||
wire->attributes["\\interface_modport"] = res.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
wire->upto = 0;
|
||||
}
|
||||
break;
|
||||
case AST_INTERFACEPORTTYPE:
|
||||
break;
|
||||
|
||||
// remember the parameter, needed for example in techmap
|
||||
|
@ -852,11 +885,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
// create an RTLIL::Wire for an AST_WIRE node
|
||||
case AST_WIRE: {
|
||||
if (current_module->wires_.count(str) != 0)
|
||||
log_error("Re-definition of signal `%s' at %s:%d!\n",
|
||||
str.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Re-definition of signal `%s'!\n", str.c_str());
|
||||
if (!range_valid)
|
||||
log_error("Signal `%s' with non-constant width at %s:%d!\n",
|
||||
str.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Signal `%s' with non-constant width!\n", str.c_str());
|
||||
|
||||
log_assert(range_left >= range_right || (range_left == -1 && range_right == 0));
|
||||
|
||||
|
@ -870,8 +901,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
|
||||
for (auto &attr : attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
||||
attr.first.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||
wire->attributes[attr.first] = attr.second->asAttrConst();
|
||||
}
|
||||
}
|
||||
|
@ -880,16 +910,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
// create an RTLIL::Memory for an AST_MEMORY node
|
||||
case AST_MEMORY: {
|
||||
if (current_module->memories.count(str) != 0)
|
||||
log_error("Re-definition of memory `%s' at %s:%d!\n",
|
||||
str.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Re-definition of memory `%s'!\n", str.c_str());
|
||||
|
||||
log_assert(children.size() >= 2);
|
||||
log_assert(children[0]->type == AST_RANGE);
|
||||
log_assert(children[1]->type == AST_RANGE);
|
||||
|
||||
if (!children[0]->range_valid || !children[1]->range_valid)
|
||||
log_error("Memory `%s' with non-constant width or size at %s:%d!\n",
|
||||
str.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Memory `%s' with non-constant width or size!\n", str.c_str());
|
||||
|
||||
RTLIL::Memory *memory = new RTLIL::Memory;
|
||||
memory->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
|
||||
|
@ -906,8 +934,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
|
||||
for (auto &attr : attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
||||
attr.first.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||
memory->attributes[attr.first] = attr.second->asAttrConst();
|
||||
}
|
||||
}
|
||||
|
@ -926,8 +953,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",
|
||||
realvalue, log_signal(sig), filename.c_str(), linenum);
|
||||
log_file_warning(filename, linenum, "converting real value %e to binary %s.\n", realvalue, log_signal(sig));
|
||||
return sig;
|
||||
}
|
||||
|
||||
|
@ -938,6 +964,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
{
|
||||
RTLIL::Wire *wire = NULL;
|
||||
RTLIL::SigChunk chunk;
|
||||
bool is_interface = false;
|
||||
|
||||
int add_undef_bits_msb = 0;
|
||||
int add_undef_bits_lsb = 0;
|
||||
|
@ -947,25 +974,48 @@ 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_file_warning(filename, linenum, "Identifier `%s' is implicitly declared.\n", str.c_str());
|
||||
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);
|
||||
log_file_error(filename, linenum, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str());
|
||||
}
|
||||
else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM) {
|
||||
if (id2ast->children[0]->type != AST_CONSTANT)
|
||||
log_error("Parameter %s does not evaluate to constant value at %s:%d!\n",
|
||||
str.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Parameter %s does not evaluate to constant value!\n", str.c_str());
|
||||
chunk = RTLIL::Const(id2ast->children[0]->bits);
|
||||
goto use_const_chunk;
|
||||
}
|
||||
else if (!id2ast || (id2ast->type != AST_WIRE && id2ast->type != AST_AUTOWIRE &&
|
||||
id2ast->type != AST_MEMORY) || current_module->wires_.count(str) == 0)
|
||||
log_error("Identifier `%s' doesn't map to any signal at %s:%d!\n",
|
||||
str.c_str(), filename.c_str(), linenum);
|
||||
else if (id2ast && (id2ast->type == AST_WIRE || id2ast->type == AST_AUTOWIRE || id2ast->type == AST_MEMORY) && current_module->wires_.count(str) != 0) {
|
||||
RTLIL::Wire *current_wire = current_module->wire(str);
|
||||
if (current_wire->get_bool_attribute("\\is_interface"))
|
||||
is_interface = true;
|
||||
// Ignore
|
||||
}
|
||||
// If an identifier is found that is not already known, assume that it is an interface:
|
||||
else if (1) { // FIXME: Check if sv_mode first?
|
||||
is_interface = true;
|
||||
}
|
||||
else {
|
||||
log_file_error(filename, linenum, "Identifier `%s' doesn't map to any signal!\n", str.c_str());
|
||||
}
|
||||
|
||||
if (id2ast->type == AST_MEMORY)
|
||||
log_error("Identifier `%s' does map to an unexpanded memory at %s:%d!\n",
|
||||
str.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Identifier `%s' does map to an unexpanded memory!\n", str.c_str());
|
||||
|
||||
// If identifier is an interface, create a RTLIL::SigSpec with a dummy wire with a attribute called 'is_interface'
|
||||
// This makes it possible for the hierarchy pass to see what are interface connections and then replace them
|
||||
// with the individual signals:
|
||||
if (is_interface) {
|
||||
RTLIL::Wire *dummy_wire;
|
||||
std::string dummy_wire_name = "$dummywireforinterface" + str;
|
||||
if (current_module->wires_.count(dummy_wire_name))
|
||||
dummy_wire = current_module->wires_[dummy_wire_name];
|
||||
else {
|
||||
dummy_wire = current_module->addWire(dummy_wire_name);
|
||||
dummy_wire->set_bool_attribute("\\is_interface");
|
||||
}
|
||||
RTLIL::SigSpec tmp = RTLIL::SigSpec(dummy_wire);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
wire = current_module->wires_[str];
|
||||
chunk.wire = wire;
|
||||
|
@ -974,7 +1024,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
|
||||
use_const_chunk:
|
||||
if (children.size() != 0) {
|
||||
log_assert(children[0]->type == AST_RANGE);
|
||||
if (children[0]->type != AST_RANGE)
|
||||
log_file_error(filename, linenum, "Single range expected.\n");
|
||||
int source_width = id2ast->range_left - id2ast->range_right + 1;
|
||||
int source_offset = id2ast->range_right;
|
||||
if (!children[0]->range_valid) {
|
||||
|
@ -983,8 +1034,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
|
||||
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
|
||||
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);
|
||||
log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
|
||||
int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
|
||||
AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ?
|
||||
children[0]->children[1]->clone() : children[0]->children[0]->clone());
|
||||
|
@ -1012,11 +1062,11 @@ 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",
|
||||
str.c_str(), filename.c_str(), linenum);
|
||||
log_file_warning(filename, linenum, "Range select out of bounds on signal `%s': Setting result bit to undef.\n",
|
||||
str.c_str());
|
||||
else
|
||||
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);
|
||||
log_file_warning(filename, linenum, "Range select [%d:%d] out of bounds on signal `%s': Setting all %d result bits to undef.\n",
|
||||
children[0]->range_left, children[0]->range_right, str.c_str(), chunk.width);
|
||||
chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width);
|
||||
} else {
|
||||
if (chunk.width + chunk.offset > source_width) {
|
||||
|
@ -1029,11 +1079,11 @@ 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",
|
||||
str.c_str(), filename.c_str(), linenum, add_undef_bits_lsb);
|
||||
log_file_warning(filename, linenum, "Range [%d:%d] select out of bounds on signal `%s': Setting %d LSB bits to undef.\n",
|
||||
children[0]->range_left, children[0]->range_right, str.c_str(), 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",
|
||||
str.c_str(), filename.c_str(), linenum, add_undef_bits_msb);
|
||||
log_file_warning(filename, linenum, "Range [%d:%d] select out of bounds on signal `%s': Setting %d MSB bits to undef.\n",
|
||||
children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_msb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1072,7 +1122,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
RTLIL::SigSpec left = children[0]->genRTLIL();
|
||||
RTLIL::SigSpec right = children[1]->genRTLIL();
|
||||
if (!left.is_fully_const())
|
||||
log_error("Left operand of replicate expression is not constant at %s:%d!\n", filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Left operand of replicate expression is not constant!\n");
|
||||
int count = left.as_int();
|
||||
RTLIL::SigSpec sig;
|
||||
for (int i = 0; i < count; i++)
|
||||
|
@ -1289,6 +1339,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0);
|
||||
cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
|
||||
|
||||
if (!sign_hint)
|
||||
is_signed = false;
|
||||
|
||||
return RTLIL::SigSpec(wire);
|
||||
}
|
||||
|
||||
|
@ -1308,7 +1361,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
int num_words = 1;
|
||||
if (type == AST_MEMINIT) {
|
||||
if (children[2]->type != AST_CONSTANT)
|
||||
log_error("Memory init with non-constant word count at %s:%d!\n", filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Memory init with non-constant word count!\n");
|
||||
num_words = int(children[2]->asInt(false));
|
||||
cell->parameters["\\WORDS"] = RTLIL::Const(num_words);
|
||||
}
|
||||
|
@ -1336,9 +1389,16 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
// generate $assert cells
|
||||
case AST_ASSERT:
|
||||
case AST_ASSUME:
|
||||
case AST_LIVE:
|
||||
case AST_FAIR:
|
||||
case AST_COVER:
|
||||
{
|
||||
const char *celltype = "$assert";
|
||||
const char *celltype = nullptr;
|
||||
if (type == AST_ASSERT) celltype = "$assert";
|
||||
if (type == AST_ASSUME) celltype = "$assume";
|
||||
if (type == AST_LIVE) celltype = "$live";
|
||||
if (type == AST_FAIR) celltype = "$fair";
|
||||
if (type == AST_COVER) celltype = "$cover";
|
||||
|
||||
log_assert(children.size() == 2);
|
||||
|
||||
|
@ -1358,8 +1418,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
|
||||
for (auto &attr : attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
||||
attr.first.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
||||
}
|
||||
|
||||
|
@ -1380,9 +1439,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
new_left.append(left[i]);
|
||||
new_right.append(right[i]);
|
||||
}
|
||||
log_warning("Ignoring assignment to constant bits at %s:%d:\n"
|
||||
log_file_warning(filename, linenum, "Ignoring assignment to constant bits:\n"
|
||||
" old assignment: %s = %s\n new assignment: %s = %s.\n",
|
||||
filename.c_str(), linenum, log_signal(left), log_signal(right),
|
||||
log_signal(left), log_signal(right),
|
||||
log_signal(new_left), log_signal(new_right));
|
||||
left = new_left;
|
||||
right = new_right;
|
||||
|
@ -1397,11 +1456,12 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
int port_counter = 0, para_counter = 0;
|
||||
|
||||
if (current_module->count_id(str) != 0)
|
||||
log_error("Re-definition of cell `%s' at %s:%d!\n",
|
||||
str.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Re-definition of cell `%s'!\n", str.c_str());
|
||||
|
||||
RTLIL::Cell *cell = current_module->addCell(str, "");
|
||||
cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
|
||||
// Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass
|
||||
cell->set_bool_attribute("\\module_not_derived");
|
||||
|
||||
for (auto it = children.begin(); it != children.end(); it++) {
|
||||
AstNode *child = *it;
|
||||
|
@ -1414,16 +1474,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
if (child->type == AST_PARASET) {
|
||||
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);
|
||||
log_file_warning(filename, linenum, "Replacing floating point parameter %s.%s = %f with string.\n",
|
||||
log_id(cell), log_id(paraname), child->children[0]->realvalue);
|
||||
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);
|
||||
log_file_error(filename, linenum, "Parameter %s.%s with non-constant value!\n",
|
||||
log_id(cell), log_id(paraname));
|
||||
cell->parameters[paraname] = child->children[0]->asParaConst();
|
||||
continue;
|
||||
}
|
||||
|
@ -1444,8 +1503,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
}
|
||||
for (auto &attr : attributes) {
|
||||
if (attr.second->type != AST_CONSTANT)
|
||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
||||
attr.first.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
||||
}
|
||||
}
|
||||
|
@ -1466,30 +1524,36 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
} break;
|
||||
|
||||
case AST_FCALL: {
|
||||
if (str == "\\$anyconst" || str == "\\$anyseq")
|
||||
if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq")
|
||||
{
|
||||
string myid = stringf("%s$%d", str.c_str() + 1, autoidx++);
|
||||
int width = width_hint;
|
||||
|
||||
if (GetSize(children) > 1)
|
||||
log_error("System function %s got %d arguments, expected 1 or 0 at %s:%d.\n",
|
||||
RTLIL::unescape_id(str).c_str(), GetSize(children), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "System function %s got %d arguments, expected 1 or 0.\n",
|
||||
RTLIL::unescape_id(str).c_str(), GetSize(children));
|
||||
|
||||
if (GetSize(children) == 1) {
|
||||
if (children[0]->type != AST_CONSTANT)
|
||||
log_error("System function %s called with non-const argument at %s:%d!\n",
|
||||
RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "System function %s called with non-const argument!\n",
|
||||
RTLIL::unescape_id(str).c_str());
|
||||
width = children[0]->asInt(true);
|
||||
}
|
||||
|
||||
if (width <= 0)
|
||||
log_error("Failed to detect width of %s at %s:%d!\n",
|
||||
RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Failed to detect width of %s!\n", RTLIL::unescape_id(str).c_str());
|
||||
|
||||
Cell *cell = current_module->addCell(myid, str.substr(1));
|
||||
cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
|
||||
cell->parameters["\\WIDTH"] = width;
|
||||
|
||||
if (attributes.count("\\reg")) {
|
||||
auto &attr = attributes.at("\\reg");
|
||||
if (attr->type != AST_CONSTANT)
|
||||
log_file_error(filename, linenum, "Attribute `reg' with non-constant value!\n");
|
||||
cell->attributes["\\reg"] = attr->asAttrConst();
|
||||
}
|
||||
|
||||
Wire *wire = current_module->addWire(myid + "_wire", width);
|
||||
wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
|
||||
cell->setPort("\\Y", wire);
|
||||
|
@ -1504,8 +1568,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
for (auto f : log_files)
|
||||
current_ast->dumpAst(f, "verilog-ast> ");
|
||||
type_name = type2str(type);
|
||||
log_error("Don't know how to generate RTLIL code for %s node at %s:%d!\n",
|
||||
type_name.c_str(), filename.c_str(), linenum);
|
||||
log_file_error(filename, linenum, "Don't know how to generate RTLIL code for %s node!\n", type_name.c_str());
|
||||
}
|
||||
|
||||
return RTLIL::SigSpec();
|
||||
|
@ -1535,4 +1598,3 @@ RTLIL::SigSpec AstNode::genWidthRTLIL(int width, const dict<RTLIL::SigBit, RTLIL
|
|||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -55,12 +55,37 @@ static bool read_next_line(char *&buffer, size_t &buffer_size, int &line_count,
|
|||
}
|
||||
}
|
||||
|
||||
void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean, bool sop_mode)
|
||||
static std::pair<RTLIL::IdString, int> wideports_split(std::string name)
|
||||
{
|
||||
int pos = -1;
|
||||
|
||||
if (name.empty() || name.back() != ']')
|
||||
goto failed;
|
||||
|
||||
for (int i = 0; i+1 < GetSize(name); i++) {
|
||||
if (name[i] == '[')
|
||||
pos = i;
|
||||
else if (name[i] < '0' || name[i] > '9')
|
||||
pos = -1;
|
||||
else if (i == pos+1 && name[i] == '0' && name[i+1] != ']')
|
||||
pos = -1;
|
||||
}
|
||||
|
||||
if (pos >= 0)
|
||||
return std::pair<RTLIL::IdString, int>("\\" + name.substr(0, pos), atoi(name.c_str() + pos+1)+1);
|
||||
|
||||
failed:
|
||||
return std::pair<RTLIL::IdString, int>("\\" + name, 0);
|
||||
}
|
||||
|
||||
void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean, bool sop_mode, bool wideports)
|
||||
{
|
||||
RTLIL::Module *module = nullptr;
|
||||
RTLIL::Const *lutptr = NULL;
|
||||
RTLIL::Cell *sopcell = NULL;
|
||||
RTLIL::Cell *lastcell = nullptr;
|
||||
RTLIL::State lut_default_state = RTLIL::State::Sx;
|
||||
std::string err_reason;
|
||||
int blif_maxnum = 0, sopmode = -1;
|
||||
|
||||
auto blif_wire = [&](const std::string &wire_name) -> Wire*
|
||||
|
@ -96,6 +121,8 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
|||
dict<RTLIL::IdString, RTLIL::Const> *obj_attributes = nullptr;
|
||||
dict<RTLIL::IdString, RTLIL::Const> *obj_parameters = nullptr;
|
||||
|
||||
dict<RTLIL::IdString, std::pair<int, bool>> wideports_cache;
|
||||
|
||||
size_t buffer_size = 4096;
|
||||
char *buffer = (char*)malloc(buffer_size);
|
||||
int line_count = 0;
|
||||
|
@ -134,6 +161,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
|||
if (module != nullptr)
|
||||
goto error;
|
||||
module = new RTLIL::Module;
|
||||
lastcell = nullptr;
|
||||
module->name = RTLIL::escape_id(strtok(NULL, " \t\r\n"));
|
||||
obj_attributes = &module->attributes;
|
||||
obj_parameters = nullptr;
|
||||
|
@ -148,7 +176,32 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
|||
|
||||
if (!strcmp(cmd, ".end"))
|
||||
{
|
||||
for (auto &wp : wideports_cache)
|
||||
{
|
||||
auto name = wp.first;
|
||||
int width = wp.second.first;
|
||||
bool isinput = wp.second.second;
|
||||
|
||||
RTLIL::Wire *wire = module->addWire(name, width);
|
||||
wire->port_input = isinput;
|
||||
wire->port_output = !isinput;
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
RTLIL::IdString other_name = name.str() + stringf("[%d]", i);
|
||||
RTLIL::Wire *other_wire = module->wire(other_name);
|
||||
if (other_wire) {
|
||||
other_wire->port_input = false;
|
||||
other_wire->port_output = false;
|
||||
if (isinput)
|
||||
module->connect(other_wire, SigSpec(wire, i));
|
||||
else
|
||||
module->connect(SigSpec(wire, i), other_wire);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module->fixup_ports();
|
||||
wideports_cache.clear();
|
||||
|
||||
if (run_clean)
|
||||
{
|
||||
|
@ -182,14 +235,17 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
|||
}
|
||||
|
||||
module = nullptr;
|
||||
lastcell = nullptr;
|
||||
obj_attributes = nullptr;
|
||||
obj_parameters = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs")) {
|
||||
if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs"))
|
||||
{
|
||||
char *p;
|
||||
while ((p = strtok(NULL, " \t\r\n")) != NULL) {
|
||||
while ((p = strtok(NULL, " \t\r\n")) != NULL)
|
||||
{
|
||||
RTLIL::IdString wire_name(stringf("\\%s", p));
|
||||
RTLIL::Wire *wire = module->wire(wire_name);
|
||||
if (wire == nullptr)
|
||||
|
@ -198,12 +254,36 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
|||
wire->port_input = true;
|
||||
else
|
||||
wire->port_output = true;
|
||||
|
||||
if (wideports) {
|
||||
std::pair<RTLIL::IdString, int> wp = wideports_split(p);
|
||||
if (wp.second > 0) {
|
||||
wideports_cache[wp.first].first = std::max(wideports_cache[wp.first].first, wp.second);
|
||||
wideports_cache[wp.first].second = !strcmp(cmd, ".inputs");
|
||||
}
|
||||
}
|
||||
}
|
||||
obj_attributes = nullptr;
|
||||
obj_parameters = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, ".cname"))
|
||||
{
|
||||
char *p = strtok(NULL, " \t\r\n");
|
||||
if (p == NULL)
|
||||
goto error;
|
||||
|
||||
if(lastcell == nullptr || module == nullptr)
|
||||
{
|
||||
err_reason = stringf("No primitive object to attach .cname %s.", p);
|
||||
goto error_with_reason;
|
||||
}
|
||||
|
||||
module->rename(lastcell, p);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, ".attr") || !strcmp(cmd, ".param")) {
|
||||
char *n = strtok(NULL, " \t\r\n");
|
||||
char *v = strtok(NULL, "\r\n");
|
||||
|
@ -221,12 +301,16 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
|||
const_v.bits[i] = v[n-i-1] != '0' ? State::S1 : State::S0;
|
||||
}
|
||||
if (!strcmp(cmd, ".attr")) {
|
||||
if (obj_attributes == nullptr)
|
||||
goto error;
|
||||
if (obj_attributes == nullptr) {
|
||||
err_reason = stringf("No object to attach .attr too.");
|
||||
goto error_with_reason;
|
||||
}
|
||||
(*obj_attributes)[id_n] = const_v;
|
||||
} else {
|
||||
if (obj_parameters == nullptr)
|
||||
goto error;
|
||||
if (obj_parameters == nullptr) {
|
||||
err_reason = stringf("No object to attach .param too.");
|
||||
goto error_with_reason;
|
||||
}
|
||||
(*obj_parameters)[id_n] = const_v;
|
||||
}
|
||||
continue;
|
||||
|
@ -271,6 +355,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
|||
}
|
||||
}
|
||||
|
||||
lastcell = cell;
|
||||
obj_attributes = &cell->attributes;
|
||||
obj_parameters = &cell->parameters;
|
||||
continue;
|
||||
|
@ -285,14 +370,45 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
|||
IdString celltype = RTLIL::escape_id(p);
|
||||
RTLIL::Cell *cell = module->addCell(NEW_ID, celltype);
|
||||
|
||||
while ((p = strtok(NULL, " \t\r\n")) != NULL) {
|
||||
dict<RTLIL::IdString, dict<int, SigBit>> cell_wideports_cache;
|
||||
|
||||
while ((p = strtok(NULL, " \t\r\n")) != NULL)
|
||||
{
|
||||
char *q = strchr(p, '=');
|
||||
if (q == NULL || !q[0])
|
||||
goto error;
|
||||
*(q++) = 0;
|
||||
|
||||
if (wideports) {
|
||||
std::pair<RTLIL::IdString, int> wp = wideports_split(p);
|
||||
if (wp.second > 0)
|
||||
cell_wideports_cache[wp.first][wp.second-1] = blif_wire(q);
|
||||
else
|
||||
cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec());
|
||||
} else {
|
||||
cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &it : cell_wideports_cache)
|
||||
{
|
||||
int width = 0;
|
||||
for (auto &b : it.second)
|
||||
width = std::max(width, b.first + 1);
|
||||
|
||||
SigSpec sig;
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
if (it.second.count(i))
|
||||
sig.append(it.second.at(i));
|
||||
else
|
||||
sig.append(module->addWire(NEW_ID));
|
||||
}
|
||||
|
||||
cell->setPort(it.first, sig);
|
||||
}
|
||||
|
||||
lastcell = cell;
|
||||
obj_attributes = &cell->attributes;
|
||||
obj_parameters = &cell->parameters;
|
||||
continue;
|
||||
|
@ -301,7 +417,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
|||
obj_attributes = nullptr;
|
||||
obj_parameters = nullptr;
|
||||
|
||||
if (!strcmp(cmd, ".barbuf"))
|
||||
if (!strcmp(cmd, ".barbuf") || !strcmp(cmd, ".conn"))
|
||||
{
|
||||
char *p = strtok(NULL, " \t\r\n");
|
||||
if (p == NULL)
|
||||
|
@ -369,6 +485,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
|||
sopcell->setPort("\\A", input_sig);
|
||||
sopcell->setPort("\\Y", output_sig);
|
||||
sopmode = -1;
|
||||
lastcell = sopcell;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -379,6 +496,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
|||
cell->setPort("\\Y", output_sig);
|
||||
lutptr = &cell->parameters.at("\\LUT");
|
||||
lut_default_state = RTLIL::State::Sx;
|
||||
lastcell = cell;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -432,7 +550,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
|||
|
||||
if (lutptr)
|
||||
{
|
||||
if (input_len > 8)
|
||||
if (input_len > 12)
|
||||
goto error;
|
||||
|
||||
for (int i = 0; i < (1 << input_len); i++) {
|
||||
|
@ -452,13 +570,17 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
|||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
log_error("Syntax error in line %d!\n", line_count);
|
||||
error_with_reason:
|
||||
log_error("Syntax error in line %d: %s\n", line_count, err_reason.c_str());
|
||||
}
|
||||
|
||||
struct BlifFrontend : public Frontend {
|
||||
BlifFrontend() : Frontend("blif", "read BLIF file") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -469,10 +591,15 @@ struct BlifFrontend : public Frontend {
|
|||
log(" -sop\n");
|
||||
log(" Create $sop cells instead of $lut cells\n");
|
||||
log("\n");
|
||||
log(" -wideports\n");
|
||||
log(" Merge ports that match the pattern 'name[int]' into a single\n");
|
||||
log(" multi-bit port 'name'.\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
bool sop_mode = false;
|
||||
bool wideports = false;
|
||||
|
||||
log_header(design, "Executing BLIF frontend.\n");
|
||||
|
||||
|
@ -483,11 +610,15 @@ struct BlifFrontend : public Frontend {
|
|||
sop_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-wideports") {
|
||||
wideports = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
parse_blif(design, *f, "", true, sop_mode);
|
||||
parse_blif(design, *f, "", true, sop_mode, wideports);
|
||||
}
|
||||
} BlifFrontend;
|
||||
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
|
||||
YOSYS_NAMESPACE_BEGIN
|
||||
|
||||
extern void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean = false, bool sop_mode = false);
|
||||
extern void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name,
|
||||
bool run_clean = false, bool sop_mode = false, bool wideports = false);
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
ilang_lexer.cc
|
||||
ilang_parser.output
|
||||
ilang_parser.tab.cc
|
||||
ilang_parser.tab.h
|
||||
ilang_parser.tab.hh
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
|
||||
GENFILES += frontends/ilang/ilang_parser.tab.cc
|
||||
GENFILES += frontends/ilang/ilang_parser.tab.h
|
||||
GENFILES += frontends/ilang/ilang_parser.tab.hh
|
||||
GENFILES += frontends/ilang/ilang_parser.output
|
||||
GENFILES += frontends/ilang/ilang_lexer.cc
|
||||
|
||||
frontends/ilang/ilang_parser.tab.cc: frontends/ilang/ilang_parser.y
|
||||
$(Q) mkdir -p $(dir $@)
|
||||
$(P) $(BISON) -d -r all -b frontends/ilang/ilang_parser $<
|
||||
$(Q) mv frontends/ilang/ilang_parser.tab.c frontends/ilang/ilang_parser.tab.cc
|
||||
$(P) $(BISON) -o $@ -d -r all -b frontends/ilang/ilang_parser $<
|
||||
|
||||
frontends/ilang/ilang_parser.tab.h: frontends/ilang/ilang_parser.tab.cc
|
||||
frontends/ilang/ilang_parser.tab.hh: frontends/ilang/ilang_parser.tab.cc
|
||||
|
||||
frontends/ilang/ilang_lexer.cc: frontends/ilang/ilang_lexer.l
|
||||
$(Q) mkdir -p $(dir $@)
|
||||
|
|
|
@ -35,7 +35,7 @@ YOSYS_NAMESPACE_BEGIN
|
|||
|
||||
struct IlangFrontend : public Frontend {
|
||||
IlangFrontend() : Frontend("ilang", "read modules from ilang file") { }
|
||||
virtual void help()
|
||||
void help() YS_OVERRIDE
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
|
@ -44,11 +44,39 @@ struct IlangFrontend : public Frontend {
|
|||
log("Load modules from an ilang file to the current design. (ilang is a text\n");
|
||||
log("representation of a design in yosys's internal format.)\n");
|
||||
log("\n");
|
||||
log(" -nooverwrite\n");
|
||||
log(" ignore re-definitions of modules. (the default behavior is to\n");
|
||||
log(" create an error message if the existing module is not a blackbox\n");
|
||||
log(" module, and overwrite the existing module if it is a blackbox module.)\n");
|
||||
log("\n");
|
||||
log(" -overwrite\n");
|
||||
log(" overwrite existing modules with the same name\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
|
||||
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||
{
|
||||
ILANG_FRONTEND::flag_nooverwrite = false;
|
||||
ILANG_FRONTEND::flag_overwrite = false;
|
||||
|
||||
log_header(design, "Executing ILANG frontend.\n");
|
||||
extra_args(f, filename, args, 1);
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
std::string arg = args[argidx];
|
||||
if (arg == "-nooverwrite") {
|
||||
ILANG_FRONTEND::flag_nooverwrite = true;
|
||||
ILANG_FRONTEND::flag_overwrite = false;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-overwrite") {
|
||||
ILANG_FRONTEND::flag_nooverwrite = false;
|
||||
ILANG_FRONTEND::flag_overwrite = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(f, filename, args, argidx);
|
||||
|
||||
log("Input filename: %s\n", filename.c_str());
|
||||
|
||||
ILANG_FRONTEND::lexin = f;
|
||||
|
|
|
@ -32,6 +32,8 @@ YOSYS_NAMESPACE_BEGIN
|
|||
namespace ILANG_FRONTEND {
|
||||
extern std::istream *lexin;
|
||||
extern RTLIL::Design *current_design;
|
||||
extern bool flag_nooverwrite;
|
||||
extern bool flag_overwrite;
|
||||
}
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#endif
|
||||
|
||||
#include "frontends/ilang/ilang_frontend.h"
|
||||
#include "ilang_parser.tab.h"
|
||||
#include "ilang_parser.tab.hh"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@ namespace ILANG_FRONTEND {
|
|||
std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack;
|
||||
std::vector<RTLIL::CaseRule*> case_stack;
|
||||
dict<RTLIL::IdString, RTLIL::Const> attrbuf;
|
||||
bool flag_nooverwrite, flag_overwrite;
|
||||
bool delete_current_module;
|
||||
}
|
||||
using namespace ILANG_FRONTEND;
|
||||
YOSYS_NAMESPACE_END
|
||||
|
@ -93,11 +95,26 @@ design:
|
|||
|
||||
module:
|
||||
TOK_MODULE TOK_ID EOL {
|
||||
if (current_design->has($2))
|
||||
delete_current_module = false;
|
||||
if (current_design->has($2)) {
|
||||
RTLIL::Module *existing_mod = current_design->module($2);
|
||||
if (!flag_overwrite && attrbuf.count("\\blackbox") && attrbuf.at("\\blackbox").as_bool()) {
|
||||
log("Ignoring blackbox re-definition of module %s.\n", $2);
|
||||
delete_current_module = true;
|
||||
} else if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute("\\blackbox")) {
|
||||
rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of module %s.", $2).c_str());
|
||||
} else if (flag_nooverwrite) {
|
||||
log("Ignoring re-definition of module %s.\n", $2);
|
||||
delete_current_module = true;
|
||||
} else {
|
||||
log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute("\\blackbox") ? " blackbox" : "", $2);
|
||||
current_design->remove(existing_mod);
|
||||
}
|
||||
}
|
||||
current_module = new RTLIL::Module;
|
||||
current_module->name = $2;
|
||||
current_module->attributes = attrbuf;
|
||||
if (!delete_current_module)
|
||||
current_design->add(current_module);
|
||||
attrbuf.clear();
|
||||
free($2);
|
||||
|
@ -105,6 +122,9 @@ module:
|
|||
if (attrbuf.size() != 0)
|
||||
rtlil_frontend_ilang_yyerror("dangling attribute");
|
||||
current_module->fixup_ports();
|
||||
if (delete_current_module)
|
||||
delete current_module;
|
||||
current_module = nullptr;
|
||||
} EOL;
|
||||
|
||||
module_body:
|
||||
|
@ -387,17 +407,13 @@ sigspec:
|
|||
$$ = new RTLIL::SigSpec(current_module->wires_[$1]);
|
||||
free($1);
|
||||
} |
|
||||
TOK_ID '[' TOK_INT ']' {
|
||||
if (current_module->wires_.count($1) == 0)
|
||||
rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str());
|
||||
$$ = new RTLIL::SigSpec(current_module->wires_[$1], $3);
|
||||
free($1);
|
||||
sigspec '[' TOK_INT ']' {
|
||||
$$ = new RTLIL::SigSpec($1->extract($3));
|
||||
delete $1;
|
||||
} |
|
||||
TOK_ID '[' TOK_INT ':' TOK_INT ']' {
|
||||
if (current_module->wires_.count($1) == 0)
|
||||
rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str());
|
||||
$$ = new RTLIL::SigSpec(current_module->wires_[$1], $5, $3 - $5 + 1);
|
||||
free($1);
|
||||
sigspec '[' TOK_INT ':' TOK_INT ']' {
|
||||
$$ = new RTLIL::SigSpec($1->extract($5, $3 - $5 + 1));
|
||||
delete $1;
|
||||
} |
|
||||
'{' sigspec_list '}' {
|
||||
$$ = $2;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
OBJS += frontends/json/jsonparse.o
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue