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
|
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 {
|
struct HelloWorldPass : public Pass {
|
||||||
HelloWorldPass() : Pass("hello_world") { }
|
HelloWorldPass() : Pass("hello_world") { }
|
||||||
virtual void execute(vector<string>, Design*) {
|
void execute(vector<string>, Design*) override {
|
||||||
log("Hello World!\n");
|
log("Hello World!\n");
|
||||||
}
|
}
|
||||||
} HelloWorldPass;
|
} HelloWorldPass;
|
||||||
|
@ -373,6 +373,7 @@ Finally run all tests with "make config-{clang,gcc,gcc-4.8}":
|
||||||
cd ~yosys
|
cd ~yosys
|
||||||
make clean
|
make clean
|
||||||
make test
|
make test
|
||||||
|
make ystests
|
||||||
make vloghtb
|
make vloghtb
|
||||||
make install
|
make install
|
||||||
|
|
||||||
|
@ -411,3 +412,98 @@ Updating the website:
|
||||||
git commit -am update
|
git commit -am update
|
||||||
make push
|
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 := emcc
|
||||||
# CONFIG := mxe
|
# CONFIG := mxe
|
||||||
# CONFIG := msys2
|
# CONFIG := msys2
|
||||||
|
# CONFIG := msys2-64
|
||||||
|
|
||||||
# features (the more the better)
|
# features (the more the better)
|
||||||
ENABLE_TCL := 1
|
ENABLE_TCL := 1
|
||||||
ENABLE_ABC := 1
|
ENABLE_ABC := 1
|
||||||
ENABLE_PLUGINS := 1
|
ENABLE_PLUGINS := 1
|
||||||
ENABLE_READLINE := 1
|
ENABLE_READLINE := 1
|
||||||
|
ENABLE_EDITLINE := 0
|
||||||
ENABLE_VERIFIC := 0
|
ENABLE_VERIFIC := 0
|
||||||
ENABLE_COVER := 1
|
ENABLE_COVER := 1
|
||||||
ENABLE_LIBYOSYS := 0
|
ENABLE_LIBYOSYS := 0
|
||||||
|
ENABLE_PROTOBUF := 0
|
||||||
|
|
||||||
# other configuration flags
|
# other configuration flags
|
||||||
|
ENABLE_GCOV := 0
|
||||||
ENABLE_GPROF := 0
|
ENABLE_GPROF := 0
|
||||||
|
ENABLE_DEBUG := 0
|
||||||
ENABLE_NDEBUG := 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
|
# clang sanitizers
|
||||||
SANITIZER =
|
SANITIZER =
|
||||||
|
@ -27,6 +37,7 @@ SANITIZER =
|
||||||
# SANITIZER = cfi
|
# SANITIZER = cfi
|
||||||
|
|
||||||
|
|
||||||
|
OS := $(shell uname -s)
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
INSTALL_SUDO :=
|
INSTALL_SUDO :=
|
||||||
|
|
||||||
|
@ -44,35 +55,52 @@ TARGETS = yosys$(EXE) yosys-config
|
||||||
PRETTY = 1
|
PRETTY = 1
|
||||||
SMALL = 0
|
SMALL = 0
|
||||||
|
|
||||||
|
# Unit test
|
||||||
|
UNITESTPATH := tests/unit
|
||||||
|
|
||||||
all: top-all
|
all: top-all
|
||||||
|
|
||||||
YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST)))
|
YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST)))
|
||||||
VPATH := $(YOSYS_SRC)
|
VPATH := $(YOSYS_SRC)
|
||||||
|
|
||||||
CXXFLAGS += -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -D_YOSYS_ -fPIC -I$(PREFIX)/include
|
CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -D_YOSYS_ -fPIC -I$(PREFIX)/include
|
||||||
LDFLAGS += -L$(LIBDIR)
|
LDFLAGS := $(LDFLAGS) -L$(LIBDIR)
|
||||||
LDLIBS = -lstdc++ -lm
|
LDLIBS := $(LDLIBS) -lstdc++ -lm
|
||||||
|
PLUGIN_LDFLAGS :=
|
||||||
|
|
||||||
PKG_CONFIG = pkg-config
|
PKG_CONFIG ?= pkg-config
|
||||||
SED = sed
|
SED ?= sed
|
||||||
BISON = bison
|
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
|
else
|
||||||
LDFLAGS += -rdynamic
|
LDFLAGS += -rdynamic
|
||||||
LDLIBS += -lrt
|
LDLIBS += -lrt
|
||||||
endif
|
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)
|
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
|
||||||
OBJS = kernel/version_$(GIT_REV).o
|
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'
|
# 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
|
# will remove the 'abc' directory and you do not want to accidentally
|
||||||
# delete your work on ABC..
|
# delete your work on ABC..
|
||||||
ABCREV = ae6716b
|
ABCREV = default
|
||||||
ABCPULL = 1
|
ABCPULL = 1
|
||||||
ABCURL ?= https://github.com/berkeley-abc/abc
|
ABCURL ?= https://github.com/berkeley-abc/abc
|
||||||
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1
|
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1
|
||||||
|
@ -105,6 +133,7 @@ ifeq ($(CONFIG),clang)
|
||||||
CXX = clang
|
CXX = clang
|
||||||
LD = clang++
|
LD = clang++
|
||||||
CXXFLAGS += -std=c++11 -Os
|
CXXFLAGS += -std=c++11 -Os
|
||||||
|
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
|
||||||
|
|
||||||
ifneq ($(SANITIZER),)
|
ifneq ($(SANITIZER),)
|
||||||
$(info [Clang Sanitizer] $(SANITIZER))
|
$(info [Clang Sanitizer] $(SANITIZER))
|
||||||
|
@ -127,16 +156,37 @@ else ifeq ($(CONFIG),gcc)
|
||||||
CXX = gcc
|
CXX = gcc
|
||||||
LD = gcc
|
LD = gcc
|
||||||
CXXFLAGS += -std=c++11 -Os
|
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)
|
else ifeq ($(CONFIG),gcc-4.8)
|
||||||
CXX = gcc-4.8
|
CXX = gcc-4.8
|
||||||
LD = gcc-4.8
|
LD = gcc-4.8
|
||||||
CXXFLAGS += -std=c++11 -Os
|
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)
|
else ifeq ($(CONFIG),emcc)
|
||||||
CXX = emcc
|
CXX = emcc
|
||||||
LD = emcc
|
LD = emcc
|
||||||
CXXFLAGS := -std=c++11 $(filter-out -fPIC -ggdb,$(CXXFLAGS))
|
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 := -Os -Wno-warn-absolute-paths
|
||||||
EMCCFLAGS += --memory-init-file 0 --embed-file share -s NO_EXIT_RUNTIME=1
|
EMCCFLAGS += --memory-init-file 0 --embed-file share -s NO_EXIT_RUNTIME=1
|
||||||
EMCCFLAGS += -s EXPORTED_FUNCTIONS="['_main','_run','_prompt','_errmsg']"
|
EMCCFLAGS += -s EXPORTED_FUNCTIONS="['_main','_run','_prompt','_errmsg']"
|
||||||
|
@ -150,6 +200,11 @@ EXE = .js
|
||||||
TARGETS := $(filter-out yosys-config,$(TARGETS))
|
TARGETS := $(filter-out yosys-config,$(TARGETS))
|
||||||
EXTRA_TARGETS += yosysjs-$(YOSYS_VER).zip
|
EXTRA_TARGETS += yosysjs-$(YOSYS_VER).zip
|
||||||
|
|
||||||
|
ifeq ($(ENABLE_ABC),1)
|
||||||
|
LINK_ABC := 1
|
||||||
|
DISABLE_ABC_THREADS := 1
|
||||||
|
endif
|
||||||
|
|
||||||
viz.js:
|
viz.js:
|
||||||
wget -O viz.js.part https://github.com/mdaines/viz.js/releases/download/0.0.3/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
|
mv viz.js.part viz.js
|
||||||
|
@ -164,29 +219,41 @@ yosys.html: misc/yosys.html
|
||||||
$(P) cp misc/yosys.html yosys.html
|
$(P) cp misc/yosys.html yosys.html
|
||||||
|
|
||||||
else ifeq ($(CONFIG),mxe)
|
else ifeq ($(CONFIG),mxe)
|
||||||
CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc
|
PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config
|
||||||
LD = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc
|
CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++
|
||||||
CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE
|
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))
|
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
||||||
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
|
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
|
||||||
LDLIBS := $(filter-out -lrt,$(LDLIBS))
|
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 += ARCHFLAGS="-DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
|
||||||
ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" ABC_USE_NO_READLINE=1 CC="$(CXX)" CXX="$(CXX)"
|
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
|
EXE = .exe
|
||||||
|
|
||||||
else ifeq ($(CONFIG),msys2)
|
else ifeq ($(CONFIG),msys2)
|
||||||
CXX = i686-w64-mingw32-gcc
|
CXX = i686-w64-mingw32-g++
|
||||||
LD = i686-w64-mingw32-gcc
|
LD = i686-w64-mingw32-g++
|
||||||
CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR
|
CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR
|
||||||
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
|
||||||
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
|
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
|
||||||
LDLIBS := $(filter-out -lrt,$(LDLIBS))
|
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 += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
|
||||||
ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" ABC_USE_NO_READLINE=0 CC="$(CXX)" CXX="$(CXX)"
|
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
|
EXE = .exe
|
||||||
|
|
||||||
else ifneq ($(CONFIG),none)
|
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
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_LIBYOSYS),1)
|
ifeq ($(ENABLE_LIBYOSYS),1)
|
||||||
|
@ -195,22 +262,67 @@ endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_READLINE),1)
|
ifeq ($(ENABLE_READLINE),1)
|
||||||
CXXFLAGS += -DYOSYS_ENABLE_READLINE
|
CXXFLAGS += -DYOSYS_ENABLE_READLINE
|
||||||
|
ifeq ($(OS), FreeBSD)
|
||||||
|
CXXFLAGS += -I/usr/local/include
|
||||||
|
endif
|
||||||
LDLIBS += -lreadline
|
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)
|
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
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(DISABLE_ABC_THREADS),1)
|
||||||
|
ABCMKARGS += "ABC_USE_NO_PTHREADS=1"
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_PLUGINS),1)
|
ifeq ($(ENABLE_PLUGINS),1)
|
||||||
CXXFLAGS += -DYOSYS_ENABLE_PLUGINS $(shell $(PKG_CONFIG) --silence-errors --cflags libffi)
|
CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags libffi) -DYOSYS_ENABLE_PLUGINS
|
||||||
LDLIBS += $(shell $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi) -ldl
|
LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi)
|
||||||
|
ifneq ($(OS), FreeBSD)
|
||||||
|
LDLIBS += -ldl
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_TCL),1)
|
ifeq ($(ENABLE_TCL),1)
|
||||||
TCL_VERSION ?= tcl$(shell bash -c "tclsh <(echo 'puts [info tclversion]')")
|
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)
|
TCL_INCLUDE ?= /usr/include/$(TCL_VERSION)
|
||||||
CXXFLAGS += -I$(TCL_INCLUDE) -DYOSYS_ENABLE_TCL
|
endif
|
||||||
LDLIBS += -l$(TCL_VERSION)
|
|
||||||
|
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
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_GPROF),1)
|
ifeq ($(ENABLE_GPROF),1)
|
||||||
|
@ -219,21 +331,44 @@ LDFLAGS += -pg
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_NDEBUG),1)
|
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
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_ABC),1)
|
ifeq ($(ENABLE_ABC),1)
|
||||||
CXXFLAGS += -DYOSYS_ENABLE_ABC
|
CXXFLAGS += -DYOSYS_ENABLE_ABC
|
||||||
|
ifeq ($(LINK_ABC),1)
|
||||||
|
CXXFLAGS += -DYOSYS_LINK_ABC
|
||||||
|
ifeq ($(DISABLE_ABC_THREADS),0)
|
||||||
|
LDLIBS += -lpthread
|
||||||
|
endif
|
||||||
|
else
|
||||||
ifeq ($(ABCEXTERNAL),)
|
ifeq ($(ABCEXTERNAL),)
|
||||||
TARGETS += yosys-abc$(EXE)
|
TARGETS += yosys-abc$(EXE)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_VERIFIC),1)
|
ifeq ($(ENABLE_VERIFIC),1)
|
||||||
VERIFIC_DIR ?= /usr/local/src/verific_lib_eval
|
VERIFIC_DIR ?= /usr/local/src/verific_lib
|
||||||
VERIFIC_COMPONENTS ?= verilog vhdl database util containers sdf
|
VERIFIC_COMPONENTS ?= verilog vhdl database util containers hier_tree
|
||||||
CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC
|
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
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_COVER),1)
|
ifeq ($(ENABLE_COVER),1)
|
||||||
|
@ -261,8 +396,8 @@ endef
|
||||||
ifeq ($(PRETTY), 1)
|
ifeq ($(PRETTY), 1)
|
||||||
P_STATUS = 0
|
P_STATUS = 0
|
||||||
P_OFFSET = 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_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 gawk "BEGIN { N=$(words $(OBJS) yosys$(EXE)); printf \"%3d\", $(P_OFFSET)+90*$(P_STATUS)/N; exit; }")%]
|
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 $@";
|
P = @echo "$(if $(findstring $@,$(TARGETS) $(EXTRA_TARGETS)),$(eval P_OFFSET = 10))$(call P_UPDATE)$(call P_SHOW) Building $@";
|
||||||
Q = @
|
Q = @
|
||||||
S = -s
|
S = -s
|
||||||
|
@ -347,6 +482,10 @@ include techlibs/common/Makefile.inc
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(LINK_ABC),1)
|
||||||
|
OBJS += yosys-libabc.a
|
||||||
|
endif
|
||||||
|
|
||||||
top-all: $(TARGETS) $(EXTRA_TARGETS)
|
top-all: $(TARGETS) $(EXTRA_TARGETS)
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo " Build successful."
|
@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
|
$(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
|
$(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
|
yosys-config: misc/yosys-config.in
|
||||||
$(P) $(SED) -e 's#@CXXFLAGS@#$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(CXXFLAGS))#;' \
|
$(P) $(SED) -e 's#@CXXFLAGS@#$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(strip $(CXXFLAGS_NOVERIFIC)))#;' \
|
||||||
-e 's#@CXX@#$(CXX)#;' -e 's#@LDFLAGS@#$(LDFLAGS)#;' -e 's#@LDLIBS@#$(LDLIBS)#;' \
|
-e 's#@CXX@#$(strip $(CXX))#;' -e 's#@LDFLAGS@#$(strip $(LDFLAGS) $(PLUGIN_LDFLAGS))#;' -e 's#@LDLIBS@#$(strip $(LDLIBS_NOVERIFIC))#;' \
|
||||||
-e 's#@BINDIR@#$(BINDIR)#;' -e 's#@DATDIR@#$(DATDIR)#;' < $< > yosys-config
|
-e 's#@BINDIR@#$(strip $(BINDIR))#;' -e 's#@DATDIR@#$(strip $(DATDIR))#;' < $< > yosys-config
|
||||||
$(Q) chmod +x yosys-config
|
$(Q) chmod +x yosys-config
|
||||||
|
|
||||||
abc/abc-$(ABCREV)$(EXE) abc/libabc-$(ABCREV).a:
|
abc/abc-$(ABCREV)$(EXE) abc/libabc-$(ABCREV).a:
|
||||||
$(P)
|
$(P)
|
||||||
ifneq ($(ABCREV),default)
|
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; \
|
echo 'REEBE: NOP pbagnvaf ybpny zbqvsvpngvbaf! Frg NOPERI=qrsnhyg va Lbflf Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \
|
||||||
fi
|
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; }; \
|
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; \
|
echo "Pulling ABC from $(ABCURL):"; set -x; \
|
||||||
test -d abc || git clone $(ABCURL) abc; \
|
test -d abc || git clone $(ABCURL) abc; \
|
||||||
|
@ -407,6 +557,9 @@ endif
|
||||||
yosys-abc$(EXE): abc/abc-$(ABCREV)$(EXE)
|
yosys-abc$(EXE): abc/abc-$(ABCREV)$(EXE)
|
||||||
$(P) cp abc/abc-$(ABCREV)$(EXE) yosys-abc$(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),)
|
ifneq ($(SEED),)
|
||||||
SEEDOPT="-S $(SEED)"
|
SEEDOPT="-S $(SEED)"
|
||||||
else
|
else
|
||||||
|
@ -425,6 +578,8 @@ test: $(TARGETS) $(EXTRA_TARGETS)
|
||||||
+cd tests/bram && bash run-test.sh $(SEEDOPT)
|
+cd tests/bram && bash run-test.sh $(SEEDOPT)
|
||||||
+cd tests/various && bash run-test.sh
|
+cd tests/various && bash run-test.sh
|
||||||
+cd tests/sat && 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 ""
|
||||||
@echo " Passed \"make test\"."
|
@echo " Passed \"make test\"."
|
||||||
@echo ""
|
@echo ""
|
||||||
|
@ -443,18 +598,44 @@ vloghtb: $(TARGETS) $(EXTRA_TARGETS)
|
||||||
@echo " Passed \"make vloghtb\"."
|
@echo " Passed \"make vloghtb\"."
|
||||||
@echo ""
|
@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: $(TARGETS) $(EXTRA_TARGETS)
|
||||||
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR)
|
$(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) mkdir -p $(DESTDIR)$(DATDIR)
|
||||||
$(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(DATDIR)/.
|
$(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(DATDIR)/.
|
||||||
ifeq ($(ENABLE_LIBYOSYS),1)
|
ifeq ($(ENABLE_LIBYOSYS),1)
|
||||||
$(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)
|
$(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)
|
||||||
|
$(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so
|
||||||
$(INSTALL_SUDO) ldconfig
|
$(INSTALL_SUDO) ldconfig
|
||||||
endif
|
endif
|
||||||
|
|
||||||
uninstall:
|
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)
|
$(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR)
|
||||||
ifeq ($(ENABLE_LIBYOSYS),1)
|
ifeq ($(ENABLE_LIBYOSYS),1)
|
||||||
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so
|
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so
|
||||||
|
@ -472,16 +653,31 @@ clean:
|
||||||
rm -rf share
|
rm -rf share
|
||||||
if test -d manual; then cd manual && sh clean.sh; fi
|
if test -d manual; then cd manual && sh clean.sh; fi
|
||||||
rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS)
|
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 -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:
|
clean-abc:
|
||||||
$(MAKE) -C abc DEP= clean
|
$(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
|
mrproper: clean
|
||||||
git clean -xdf
|
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:
|
qtcreator:
|
||||||
{ for file in $(basename $(OBJS)); do \
|
{ for file in $(basename $(OBJS)); do \
|
||||||
for prefix in cc y l; do if [ -f $${file}.$${prefix} ]; then echo $$file.$${prefix}; fi; done \
|
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
|
config-gcc: clean
|
||||||
echo 'CONFIG := gcc' > Makefile.conf
|
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
|
config-gcc-4.8: clean
|
||||||
echo 'CONFIG := gcc-4.8' > Makefile.conf
|
echo 'CONFIG := gcc-4.8' > Makefile.conf
|
||||||
|
|
||||||
|
@ -533,13 +735,22 @@ config-emcc: clean
|
||||||
|
|
||||||
config-mxe: clean
|
config-mxe: clean
|
||||||
echo 'CONFIG := mxe' > Makefile.conf
|
echo 'CONFIG := mxe' > Makefile.conf
|
||||||
echo 'ENABLE_TCL := 0' >> Makefile.conf
|
|
||||||
echo 'ENABLE_PLUGINS := 0' >> Makefile.conf
|
echo 'ENABLE_PLUGINS := 0' >> Makefile.conf
|
||||||
echo 'ENABLE_READLINE := 0' >> Makefile.conf
|
|
||||||
|
|
||||||
config-msys2: clean
|
config-msys2: clean
|
||||||
echo 'CONFIG := msys2' > Makefile.conf
|
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
|
config-gprof: clean
|
||||||
echo 'CONFIG := gcc' > Makefile.conf
|
echo 'CONFIG := gcc' > Makefile.conf
|
||||||
echo 'ENABLE_GPROF := 1' >> Makefile.conf
|
echo 'ENABLE_GPROF := 1' >> Makefile.conf
|
||||||
|
@ -560,6 +771,6 @@ echo-git-rev:
|
||||||
-include kernel/*.d
|
-include kernel/*.d
|
||||||
-include techlibs/*/*.d
|
-include techlibs/*/*.d
|
||||||
|
|
||||||
.PHONY: all top-all abc test install install-abc manual clean mrproper qtcreator
|
.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-4.8 config-gprof config-sudo
|
.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 impltf_mode;
|
||||||
bool gates_mode;
|
bool gates_mode;
|
||||||
bool cname_mode;
|
bool cname_mode;
|
||||||
|
bool iname_mode;
|
||||||
bool param_mode;
|
bool param_mode;
|
||||||
bool attr_mode;
|
bool attr_mode;
|
||||||
|
bool iattr_mode;
|
||||||
bool blackbox_mode;
|
bool blackbox_mode;
|
||||||
bool noalias_mode;
|
bool noalias_mode;
|
||||||
|
|
||||||
|
@ -48,7 +50,8 @@ struct BlifDumperConfig
|
||||||
std::string true_type, true_out, false_type, false_out, undef_type, undef_out;
|
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),
|
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
|
struct BlifDumper
|
||||||
|
@ -112,7 +115,7 @@ struct BlifDumper
|
||||||
str[i] = '?';
|
str[i] = '?';
|
||||||
|
|
||||||
if (sig.wire->width != 1)
|
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);
|
cstr_buf.push_back(str);
|
||||||
return cstr_buf.back().c_str();
|
return cstr_buf.back().c_str();
|
||||||
|
@ -240,106 +243,118 @@ struct BlifDumper
|
||||||
if (!config->icells_mode && cell->type == "$_NOT_") {
|
if (!config->icells_mode && cell->type == "$_NOT_") {
|
||||||
f << stringf(".names %s %s\n0 1\n",
|
f << stringf(".names %s %s\n0 1\n",
|
||||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\Y")));
|
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\Y")));
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->icells_mode && cell->type == "$_AND_") {
|
if (!config->icells_mode && cell->type == "$_AND_") {
|
||||||
f << stringf(".names %s %s %s\n11 1\n",
|
f << stringf(".names %s %s %s\n11 1\n",
|
||||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->icells_mode && cell->type == "$_OR_") {
|
if (!config->icells_mode && cell->type == "$_OR_") {
|
||||||
f << stringf(".names %s %s %s\n1- 1\n-1 1\n",
|
f << stringf(".names %s %s %s\n1- 1\n-1 1\n",
|
||||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->icells_mode && cell->type == "$_XOR_") {
|
if (!config->icells_mode && cell->type == "$_XOR_") {
|
||||||
f << stringf(".names %s %s %s\n10 1\n01 1\n",
|
f << stringf(".names %s %s %s\n10 1\n01 1\n",
|
||||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->icells_mode && cell->type == "$_NAND_") {
|
if (!config->icells_mode && cell->type == "$_NAND_") {
|
||||||
f << stringf(".names %s %s %s\n0- 1\n-0 1\n",
|
f << stringf(".names %s %s %s\n0- 1\n-0 1\n",
|
||||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->icells_mode && cell->type == "$_NOR_") {
|
if (!config->icells_mode && cell->type == "$_NOR_") {
|
||||||
f << stringf(".names %s %s %s\n00 1\n",
|
f << stringf(".names %s %s %s\n00 1\n",
|
||||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->icells_mode && cell->type == "$_XNOR_") {
|
if (!config->icells_mode && cell->type == "$_XNOR_") {
|
||||||
f << stringf(".names %s %s %s\n11 1\n00 1\n",
|
f << stringf(".names %s %s %s\n11 1\n00 1\n",
|
||||||
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
|
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_") {
|
if (!config->icells_mode && cell->type == "$_AOI3_") {
|
||||||
f << stringf(".names %s %s %s %s\n-00 1\n0-0 1\n",
|
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")));
|
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_") {
|
if (!config->icells_mode && cell->type == "$_OAI3_") {
|
||||||
f << stringf(".names %s %s %s %s\n00- 1\n--0 1\n",
|
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")));
|
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_") {
|
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",
|
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("\\A")), cstr(cell->getPort("\\B")),
|
||||||
cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y")));
|
cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y")));
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->icells_mode && cell->type == "$_OAI4_") {
|
if (!config->icells_mode && cell->type == "$_OAI4_") {
|
||||||
f << stringf(".names %s %s %s %s %s\n00-- 1\n--00 1\n",
|
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("\\A")), cstr(cell->getPort("\\B")),
|
||||||
cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y")));
|
cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y")));
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->icells_mode && cell->type == "$_MUX_") {
|
if (!config->icells_mode && cell->type == "$_MUX_") {
|
||||||
f << stringf(".names %s %s %s %s\n1-0 1\n-11 1\n",
|
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("\\A")), cstr(cell->getPort("\\B")),
|
||||||
cstr(cell->getPort("\\S")), cstr(cell->getPort("\\Y")));
|
cstr(cell->getPort("\\S")), cstr(cell->getPort("\\Y")));
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->icells_mode && cell->type == "$_FF_") {
|
if (!config->icells_mode && cell->type == "$_FF_") {
|
||||||
f << stringf(".latch %s %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
|
f << stringf(".latch %s %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
|
||||||
cstr_init(cell->getPort("\\Q")));
|
cstr_init(cell->getPort("\\Q")));
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->icells_mode && cell->type == "$_DFF_N_") {
|
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")),
|
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")));
|
cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q")));
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->icells_mode && cell->type == "$_DFF_P_") {
|
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")),
|
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")));
|
cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q")));
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->icells_mode && cell->type == "$_DLATCH_N_") {
|
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")),
|
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")));
|
cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q")));
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->icells_mode && cell->type == "$_DLATCH_P_") {
|
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")),
|
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")));
|
cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q")));
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->icells_mode && cell->type == "$lut") {
|
if (!config->icells_mode && cell->type == "$lut") {
|
||||||
|
@ -361,7 +376,7 @@ struct BlifDumper
|
||||||
}
|
}
|
||||||
f << " 1\n";
|
f << " 1\n";
|
||||||
}
|
}
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->icells_mode && cell->type == "$sop") {
|
if (!config->icells_mode && cell->type == "$sop") {
|
||||||
|
@ -389,7 +404,7 @@ struct BlifDumper
|
||||||
}
|
}
|
||||||
f << " 1\n";
|
f << " 1\n";
|
||||||
}
|
}
|
||||||
continue;
|
goto internal_cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
f << stringf(".%s %s", subckt_or_gate(cell->type.str()), cstr(cell->type));
|
f << stringf(".%s %s", subckt_or_gate(cell->type.str()), cstr(cell->type));
|
||||||
|
@ -409,6 +424,14 @@ struct BlifDumper
|
||||||
dump_params(".attr", cell->attributes);
|
dump_params(".attr", cell->attributes);
|
||||||
if (config->param_mode)
|
if (config->param_mode)
|
||||||
dump_params(".param", cell->parameters);
|
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())
|
for (auto &conn : module->connections())
|
||||||
|
@ -441,7 +464,7 @@ struct BlifDumper
|
||||||
|
|
||||||
struct BlifBackend : public Backend {
|
struct BlifBackend : public Backend {
|
||||||
BlifBackend() : Backend("blif", "write design to BLIF file") { }
|
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---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -499,6 +522,11 @@ struct BlifBackend : public Backend {
|
||||||
log(" -cname\n");
|
log(" -cname\n");
|
||||||
log(" use the non-standard .cname statement to write cell names\n");
|
log(" use the non-standard .cname statement to write cell names\n");
|
||||||
log("\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(" -blackbox\n");
|
||||||
log(" write blackbox cells with .blackbox statement.\n");
|
log(" write blackbox cells with .blackbox statement.\n");
|
||||||
log("\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(" do not write definitions for the $true, $false and $undef wires.\n");
|
||||||
log("\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 top_module_name;
|
||||||
std::string buf_type, buf_in, buf_out;
|
std::string buf_type, buf_in, buf_out;
|
||||||
|
@ -575,6 +603,14 @@ struct BlifBackend : public Backend {
|
||||||
config.attr_mode = true;
|
config.attr_mode = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-iname") {
|
||||||
|
config.iname_mode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-iattr") {
|
||||||
|
config.iattr_mode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (args[argidx] == "-blackbox") {
|
if (args[argidx] == "-blackbox") {
|
||||||
config.blackbox_mode = true;
|
config.blackbox_mode = true;
|
||||||
continue;
|
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
|
PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
#define EDIF_DEF(_id) edif_names(RTLIL::unescape_id(_id), true).c_str()
|
#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()
|
#define EDIF_REF(_id) edif_names(RTLIL::unescape_id(_id), false).c_str()
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
struct EdifNames
|
struct EdifNames
|
||||||
{
|
{
|
||||||
int counter;
|
int counter;
|
||||||
|
char delim_left, delim_right;
|
||||||
std::set<std::string> generated_names, used_names;
|
std::set<std::string> generated_names, used_names;
|
||||||
std::map<std::string, std::string> name_map;
|
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) {
|
if (define) {
|
||||||
std::string new_id = operator()(id, false);
|
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;
|
return new_id != id ? stringf("(rename %s \"%s\")", new_id.c_str(), id.c_str()) : id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,11 +87,10 @@ namespace
|
||||||
return gen_name;
|
return gen_name;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
struct EdifBackend : public Backend {
|
struct EdifBackend : public Backend {
|
||||||
EdifBackend() : Backend("edif", "write design to EDIF netlist file") { }
|
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---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
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(" if the design contains constant nets. use \"hilomap\" to map to custom\n");
|
||||||
log(" constant drivers first)\n");
|
log(" constant drivers first)\n");
|
||||||
log("\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("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("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("necessary to make small modifications to this command when a different tool\n");
|
||||||
log("is targeted.\n");
|
log("is targeted.\n");
|
||||||
log("\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");
|
log_header(design, "Executing EDIF backend.\n");
|
||||||
|
|
||||||
std::string top_module_name;
|
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;
|
std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports;
|
||||||
bool nogndvcc = false;
|
bool nogndvcc = false;
|
||||||
CellTypes ct(design);
|
CellTypes ct(design);
|
||||||
|
@ -132,6 +141,23 @@ struct EdifBackend : public Backend {
|
||||||
nogndvcc = true;
|
nogndvcc = true;
|
||||||
continue;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(f, filename, args, argidx);
|
extra_args(f, filename, args, argidx);
|
||||||
|
@ -214,8 +240,18 @@ struct EdifBackend : public Backend {
|
||||||
}
|
}
|
||||||
if (port_it.second == 1)
|
if (port_it.second == 1)
|
||||||
*f << stringf(" (port %s (direction %s))\n", EDIF_DEF(port_it.first), dir);
|
*f << stringf(" (port %s (direction %s))\n", EDIF_DEF(port_it.first), dir);
|
||||||
else
|
else {
|
||||||
*f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEF(port_it.first), port_it.second, dir);
|
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");
|
||||||
*f << stringf(" )\n");
|
*f << stringf(" )\n");
|
||||||
|
@ -283,10 +319,13 @@ struct EdifBackend : public Backend {
|
||||||
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire));
|
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire));
|
||||||
net_join_db[sig].insert(stringf("(portRef %s)", EDIF_REF(wire->name)));
|
net_join_db[sig].insert(stringf("(portRef %s)", EDIF_REF(wire->name)));
|
||||||
} else {
|
} 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++) {
|
for (int i = 0; i < wire->width; i++) {
|
||||||
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, 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(" (instance %s\n", EDIF_DEF(cell->name));
|
||||||
*f << stringf(" (viewRef VIEW_NETLIST (cellRef %s%s))", EDIF_REF(cell->type),
|
*f << stringf(" (viewRef VIEW_NETLIST (cellRef %s%s))", EDIF_REF(cell->type),
|
||||||
lib_cell_ports.count(cell->type) > 0 ? " (libraryRef LIB)" : "");
|
lib_cell_ports.count(cell->type) > 0 ? " (libraryRef LIB)" : "");
|
||||||
for (auto &p : cell->parameters)
|
|
||||||
if ((p.second.flags & RTLIL::CONST_FLAG_STRING) != 0)
|
auto add_prop = [&](IdString name, Const val) {
|
||||||
*f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(p.first), p.second.decode_string().c_str());
|
if ((val.flags & RTLIL::CONST_FLAG_STRING) != 0)
|
||||||
else if (p.second.bits.size() <= 32 && RTLIL::SigSpec(p.second).is_fully_def())
|
*f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(name), val.decode_string().c_str());
|
||||||
*f << stringf("\n (property %s (integer %u))", EDIF_DEF(p.first), p.second.as_int());
|
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 {
|
else {
|
||||||
std::string hex_string = "";
|
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;
|
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+0 < val.bits.size() && val.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+1 < val.bits.size() && val.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+2 < val.bits.size() && val.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+3 < val.bits.size() && val.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8;
|
||||||
char digit_str[2] = { "0123456789abcdef"[digit_value], 0 };
|
char digit_str[2] = { "0123456789abcdef"[digit_value], 0 };
|
||||||
hex_string = std::string(digit_str) + hex_string;
|
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");
|
*f << stringf(")\n");
|
||||||
for (auto &p : cell->connections()) {
|
for (auto &p : cell->connections()) {
|
||||||
RTLIL::SigSpec sig = sigmap(p.second);
|
RTLIL::SigSpec sig = sigmap(p.second);
|
||||||
for (int i = 0; i < GetSize(sig); i++)
|
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)));
|
net_join_db[sig[i]].insert(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)));
|
||||||
else
|
else {
|
||||||
net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))", EDIF_REF(p.first), i, EDIF_REF(cell->name)));
|
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) {
|
for (auto &it : net_join_db) {
|
||||||
RTLIL::SigBit sig = it.first;
|
RTLIL::SigBit sig = it.first;
|
||||||
if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1)
|
if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1) {
|
||||||
continue;
|
if (sig == RTLIL::State::Sx) {
|
||||||
std::string netname = log_signal(sig);
|
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++)
|
for (size_t i = 0; i < netname.size(); i++)
|
||||||
if (netname[i] == ' ' || netname[i] == '\\')
|
if (netname[i] == ' ' || netname[i] == '\\')
|
||||||
netname.erase(netname.begin() + i--);
|
netname.erase(netname.begin() + i--);
|
||||||
|
}
|
||||||
*f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
|
*f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
|
||||||
for (auto &ref : it.second)
|
for (auto &ref : it.second)
|
||||||
*f << stringf(" %s\n", ref.c_str());
|
*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());
|
f << stringf("%s" "sync ", indent.c_str());
|
||||||
switch (sy->type) {
|
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::ST1: f << stringf("high ");
|
||||||
if (0) case RTLIL::STp: f << stringf("posedge ");
|
if (0) case RTLIL::STp: f << stringf("posedge ");
|
||||||
if (0) case RTLIL::STn: f << stringf("negedge ");
|
if (0) case RTLIL::STn: f << stringf("negedge ");
|
||||||
|
@ -382,7 +382,7 @@ PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
struct IlangBackend : public Backend {
|
struct IlangBackend : public Backend {
|
||||||
IlangBackend() : Backend("ilang", "write design to ilang file") { }
|
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---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -395,7 +395,7 @@ struct IlangBackend : public Backend {
|
||||||
log(" only write selected parts of the design.\n");
|
log(" only write selected parts of the design.\n");
|
||||||
log("\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;
|
bool selected = false;
|
||||||
|
|
||||||
|
@ -422,7 +422,7 @@ struct IlangBackend : public Backend {
|
||||||
|
|
||||||
struct DumpPass : public Pass {
|
struct DumpPass : public Pass {
|
||||||
DumpPass() : Pass("dump", "print parts of the design in ilang format") { }
|
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---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -445,7 +445,7 @@ struct DumpPass : public Pass {
|
||||||
log(" like -outfile but append instead of overwrite\n");
|
log(" like -outfile but append instead of overwrite\n");
|
||||||
log("\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;
|
std::string filename;
|
||||||
bool flag_m = false, flag_n = false, append = false;
|
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 {
|
struct IntersynthBackend : public Backend {
|
||||||
IntersynthBackend() : Backend("intersynth", "write design to InterSynth netlist file") { }
|
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---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -71,7 +71,7 @@ struct IntersynthBackend : public Backend {
|
||||||
log("http://www.clifford.at/intersynth/\n");
|
log("http://www.clifford.at/intersynth/\n");
|
||||||
log("\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_header(design, "Executing INTERSYNTH backend.\n");
|
||||||
log_push();
|
log_push();
|
||||||
|
|
|
@ -93,8 +93,10 @@ struct JsonWriter
|
||||||
f << get_string(param.second.decode_string());
|
f << get_string(param.second.decode_string());
|
||||||
else if (GetSize(param.second.bits) > 32)
|
else if (GetSize(param.second.bits) > 32)
|
||||||
f << get_string(param.second.as_string());
|
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());
|
f << stringf("%d", param.second.as_int());
|
||||||
|
else
|
||||||
|
f << stringf("%u", param.second.as_int());
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,6 +203,8 @@ struct JsonWriter
|
||||||
void write_design(Design *design_)
|
void write_design(Design *design_)
|
||||||
{
|
{
|
||||||
design = design_;
|
design = design_;
|
||||||
|
design->sort();
|
||||||
|
|
||||||
f << stringf("{\n");
|
f << stringf("{\n");
|
||||||
f << stringf(" \"creator\": %s,\n", get_string(yosys_version_str).c_str());
|
f << stringf(" \"creator\": %s,\n", get_string(yosys_version_str).c_str());
|
||||||
f << stringf(" \"modules\": {\n");
|
f << stringf(" \"modules\": {\n");
|
||||||
|
@ -248,7 +252,7 @@ struct JsonWriter
|
||||||
|
|
||||||
struct JsonBackend : public Backend {
|
struct JsonBackend : public Backend {
|
||||||
JsonBackend() : Backend("json", "write design to a JSON file") { }
|
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---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
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("connected to a constant driver are denoted as string \"0\" or \"1\" instead of\n");
|
||||||
log("a number.\n");
|
log("a number.\n");
|
||||||
log("\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("For example the following Verilog code:\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" module test(input x, y);\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("format. A program processing this format must ignore all unknown fields.\n");
|
||||||
log("\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;
|
bool aig_mode = false;
|
||||||
|
|
||||||
|
@ -476,7 +484,7 @@ struct JsonBackend : public Backend {
|
||||||
|
|
||||||
struct JsonPass : public Pass {
|
struct JsonPass : public Pass {
|
||||||
JsonPass() : Pass("json", "write design in JSON format") { }
|
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---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
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("See 'help write_json' for a description of the JSON format used.\n");
|
||||||
log("\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;
|
std::string filename;
|
||||||
bool aig_mode = false;
|
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
|
TARGETS += yosys-smtbmc
|
||||||
|
|
||||||
yosys-smtbmc: backends/smt2/smtbmc.py
|
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) chmod +x $@.new
|
||||||
$(Q) mv $@.new $@
|
$(Q) mv $@.new $@
|
||||||
|
|
||||||
|
|
|
@ -32,14 +32,18 @@ struct Smt2Worker
|
||||||
CellTypes ct;
|
CellTypes ct;
|
||||||
SigMap sigmap;
|
SigMap sigmap;
|
||||||
RTLIL::Module *module;
|
RTLIL::Module *module;
|
||||||
bool bvmode, memmode, wiresmode, verbose;
|
bool bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode;
|
||||||
int idcounter;
|
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::map<RTLIL::SigBit, RTLIL::Cell*> bit_driver;
|
||||||
std::set<RTLIL::Cell*> exported_cells, hiercells, hiercells_queue;
|
std::set<RTLIL::Cell*> exported_cells, hiercells, hiercells_queue;
|
||||||
pool<Cell*> recursive_cells, registers;
|
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<RTLIL::SigBit, std::pair<int, int>> fcache;
|
||||||
std::map<Cell*, int> memarrays;
|
std::map<Cell*, int> memarrays;
|
||||||
std::map<int, int> bvsizes;
|
std::map<int, int> bvsizes;
|
||||||
|
@ -63,17 +67,64 @@ struct Smt2Worker
|
||||||
return get_id(obj->name);
|
return get_id(obj->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool wiresmode, bool verbose) :
|
void makebits(std::string name, int width = 0, std::string comment = std::string())
|
||||||
ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode),
|
|
||||||
wiresmode(wiresmode), verbose(verbose), idcounter(0)
|
|
||||||
{
|
{
|
||||||
decls.push_back(stringf("(declare-sort |%s_s| 0)\n", get_id(module)));
|
std::string decl_str;
|
||||||
decls.push_back(stringf("(declare-fun |%s_is| (|%s_s|) Bool)\n", get_id(module), get_id(module)));
|
|
||||||
|
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 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_input = ct.cell_input(cell->type, conn.first);
|
||||||
bool is_output = ct.cell_output(cell->type, conn.first);
|
bool is_output = ct.cell_output(cell->type, conn.first);
|
||||||
|
|
||||||
if (is_output && !is_input)
|
if (is_output && !is_input)
|
||||||
for (auto bit : sigmap(conn.second)) {
|
for (auto bit : sigmap(conn.second)) {
|
||||||
if (bit_driver.count(bit))
|
if (bit_driver.count(bit))
|
||||||
|
@ -83,6 +134,66 @@ struct Smt2Worker
|
||||||
else if (is_output || !is_input)
|
else if (is_output || !is_input)
|
||||||
log_error("Unsupported or unknown directionality on port %s of cell %s.%s (%s).\n",
|
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));
|
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 (fcache.count(bit) == 0) {
|
||||||
if (verbose) log("%*s-> external bool: %s\n", 2+2*GetSize(recursive_cells), "",
|
if (verbose) log("%*s-> external bool: %s\n", 2+2*GetSize(recursive_cells), "",
|
||||||
log_signal(bit));
|
log_signal(bit));
|
||||||
decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n",
|
makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(bit));
|
||||||
get_id(module), idcounter, get_id(module), log_signal(bit)));
|
|
||||||
register_bool(bit, idcounter++);
|
register_bool(bit, idcounter++);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,8 +347,7 @@ struct Smt2Worker
|
||||||
log_signal(sig.extract(i, j)));
|
log_signal(sig.extract(i, j)));
|
||||||
for (auto bit : sig.extract(i, j))
|
for (auto bit : sig.extract(i, j))
|
||||||
log_assert(bit_driver.count(bit) == 0);
|
log_assert(bit_driver.count(bit) == 0);
|
||||||
decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n",
|
makebits(stringf("%s#%d", get_id(module), idcounter), j, log_signal(sig.extract(i, j)));
|
||||||
get_id(module), idcounter, get_id(module), j, log_signal(sig.extract(i, j))));
|
|
||||||
subexpr.push_back(stringf("(|%s#%d| %s)", get_id(module), idcounter, state_name));
|
subexpr.push_back(stringf("(|%s#%d| %s)", get_id(module), idcounter, state_name));
|
||||||
register_bv(sig.extract(i, j), idcounter++);
|
register_bv(sig.extract(i, j), idcounter++);
|
||||||
}
|
}
|
||||||
|
@ -288,6 +397,7 @@ struct Smt2Worker
|
||||||
|
|
||||||
if (type == 's' || type == 'd' || type == 'b') {
|
if (type == 's' || type == 'd' || type == 'b') {
|
||||||
width = max(width, GetSize(cell->getPort("\\A")));
|
width = max(width, GetSize(cell->getPort("\\A")));
|
||||||
|
if (cell->hasPort("\\B"))
|
||||||
width = max(width, GetSize(cell->getPort("\\B")));
|
width = max(width, GetSize(cell->getPort("\\B")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,8 +492,7 @@ struct Smt2Worker
|
||||||
if (cell->type.in("$_FF_", "$_DFF_P_", "$_DFF_N_"))
|
if (cell->type.in("$_FF_", "$_DFF_P_", "$_DFF_N_"))
|
||||||
{
|
{
|
||||||
registers.insert(cell);
|
registers.insert(cell);
|
||||||
decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n",
|
makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(cell->getPort("\\Q")));
|
||||||
get_id(module), idcounter, get_id(module), log_signal(cell->getPort("\\Q"))));
|
|
||||||
register_bool(cell->getPort("\\Q"), idcounter++);
|
register_bool(cell->getPort("\\Q"), idcounter++);
|
||||||
recursive_cells.erase(cell);
|
recursive_cells.erase(cell);
|
||||||
return;
|
return;
|
||||||
|
@ -397,6 +506,8 @@ struct Smt2Worker
|
||||||
if (cell->type == "$_NOR_") return export_gate(cell, "(not (or A B))");
|
if (cell->type == "$_NOR_") return export_gate(cell, "(not (or A B))");
|
||||||
if (cell->type == "$_XOR_") return export_gate(cell, "(xor A B)");
|
if (cell->type == "$_XOR_") return export_gate(cell, "(xor A B)");
|
||||||
if (cell->type == "$_XNOR_") return export_gate(cell, "(not (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 == "$_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 == "$_AOI3_") return export_gate(cell, "(not (or (and A B) C))");
|
||||||
if (cell->type == "$_OAI3_") return export_gate(cell, "(not (and (or A B) C))");
|
if (cell->type == "$_OAI3_") return export_gate(cell, "(not (and (or A B) C))");
|
||||||
|
@ -410,20 +521,22 @@ struct Smt2Worker
|
||||||
if (cell->type.in("$ff", "$dff"))
|
if (cell->type.in("$ff", "$dff"))
|
||||||
{
|
{
|
||||||
registers.insert(cell);
|
registers.insert(cell);
|
||||||
decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n",
|
makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort("\\Q")), log_signal(cell->getPort("\\Q")));
|
||||||
get_id(module), idcounter, get_id(module), GetSize(cell->getPort("\\Q")), log_signal(cell->getPort("\\Q"))));
|
|
||||||
register_bv(cell->getPort("\\Q"), idcounter++);
|
register_bv(cell->getPort("\\Q"), idcounter++);
|
||||||
recursive_cells.erase(cell);
|
recursive_cells.erase(cell);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->type.in("$anyconst", "$anyseq"))
|
if (cell->type.in("$anyconst", "$anyseq", "$allconst", "$allseq"))
|
||||||
{
|
{
|
||||||
registers.insert(cell);
|
registers.insert(cell);
|
||||||
decls.push_back(stringf("; yosys-smt2-%s %s#%d %s\n", cell->type.c_str() + 1, get_id(module), idcounter,
|
string infostr = cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell);
|
||||||
cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell)));
|
if (cell->attributes.count("\\reg"))
|
||||||
decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n",
|
infostr += " " + cell->attributes.at("\\reg").decode_string();
|
||||||
get_id(module), idcounter, get_id(module), GetSize(cell->getPort("\\Y")), log_signal(cell->getPort("\\Y"))));
|
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++);
|
register_bv(cell->getPort("\\Y"), idcounter++);
|
||||||
recursive_cells.erase(cell);
|
recursive_cells.erase(cell);
|
||||||
return;
|
return;
|
||||||
|
@ -441,7 +554,9 @@ struct Smt2Worker
|
||||||
|
|
||||||
if (cell->type.in("$shift", "$shiftx")) {
|
if (cell->type.in("$shift", "$shiftx")) {
|
||||||
if (cell->getParam("\\B_SIGNED").as_bool()) {
|
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 {
|
} else {
|
||||||
return export_bvop(cell, "(bvlshr A B)", 's');
|
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 == "$div") return export_bvop(cell, "(bvUdiv A B)", 'd');
|
||||||
if (cell->type == "$mod") return export_bvop(cell, "(bvUrem 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_and") return export_reduce(cell, "(and A)", true);
|
||||||
if (cell->type == "$reduce_or") return export_reduce(cell, "(or A)", false);
|
if (cell->type == "$reduce_or") return export_reduce(cell, "(or A)", false);
|
||||||
if (cell->type == "$reduce_xor") return export_reduce(cell, "(xor A)", false);
|
if (cell->type == "$reduce_xor") return export_reduce(cell, "(xor A)", false);
|
||||||
|
@ -513,13 +635,32 @@ struct Smt2Worker
|
||||||
int abits = cell->getParam("\\ABITS").as_int();
|
int abits = cell->getParam("\\ABITS").as_int();
|
||||||
int width = cell->getParam("\\WIDTH").as_int();
|
int width = cell->getParam("\\WIDTH").as_int();
|
||||||
int rd_ports = cell->getParam("\\RD_PORTS").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",
|
bool async_read = false;
|
||||||
get_id(module), arrayid, get_id(module), abits, width, get_id(cell)));
|
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("; yosys-smt2-memory %s %d %d %d %d %s\n", get_id(cell), abits, width, rd_ports, wr_ports, async_read ? "async" : "sync"));
|
||||||
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));
|
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++)
|
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\"! "
|
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));
|
"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)));
|
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",
|
std::string read_expr = "#b";
|
||||||
get_id(module), idcounter, get_id(module), width, get_id(module), arrayid, addr.c_str(), log_signal(data_sig)));
|
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++);
|
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);
|
registers.insert(cell);
|
||||||
recursive_cells.erase(cell);
|
recursive_cells.erase(cell);
|
||||||
|
@ -553,30 +742,36 @@ struct Smt2Worker
|
||||||
|
|
||||||
for (auto &conn : cell->connections())
|
for (auto &conn : cell->connections())
|
||||||
{
|
{
|
||||||
|
if (GetSize(conn.second) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
Wire *w = m->wire(conn.first);
|
Wire *w = m->wire(conn.first);
|
||||||
SigSpec sig = sigmap(conn.second);
|
SigSpec sig = sigmap(conn.second);
|
||||||
|
|
||||||
if (w->port_output && !w->port_input) {
|
if (w->port_output && !w->port_input) {
|
||||||
if (GetSize(w) > 1) {
|
if (GetSize(w) > 1) {
|
||||||
if (bvmode) {
|
if (bvmode) {
|
||||||
decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n",
|
makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(w), log_signal(sig));
|
||||||
get_id(module), idcounter, get_id(module), GetSize(w), log_signal(sig)));
|
|
||||||
register_bv(sig, idcounter++);
|
register_bv(sig, idcounter++);
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < GetSize(w); i++) {
|
for (int i = 0; i < GetSize(w); i++) {
|
||||||
decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n",
|
makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(sig[i]));
|
||||||
get_id(module), idcounter, get_id(module), log_signal(sig[i])));
|
|
||||||
register_bool(sig[i], idcounter++);
|
register_bool(sig[i], idcounter++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n",
|
makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(sig));
|
||||||
get_id(module), idcounter, get_id(module), log_signal(sig)));
|
|
||||||
register_bool(sig, idcounter++);
|
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",
|
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)));
|
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));
|
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] == '\\'))
|
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));
|
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) {
|
if (bvmode && GetSize(sig) > 1) {
|
||||||
decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) (_ BitVec %d) %s)\n",
|
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()));
|
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 {
|
} else {
|
||||||
for (int i = 0; i < GetSize(sig); i++)
|
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",
|
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()));
|
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",
|
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()));
|
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")) {
|
if (wire->attributes.count("\\init")) {
|
||||||
RTLIL::SigSpec sig = sigmap(wire);
|
RTLIL::SigSpec sig = sigmap(wire);
|
||||||
Const val = wire->attributes.at("\\init");
|
Const val = wire->attributes.at("\\init");
|
||||||
val.bits.resize(GetSize(sig));
|
val.bits.resize(GetSize(sig), State::Sx);
|
||||||
if (bvmode && GetSize(sig) > 1) {
|
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)));
|
init_list.push_back(stringf("(= %s #b%s) ; %s", get_bv(sig).c_str(), val.as_string().c_str(), get_id(wire)));
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < GetSize(sig); i++)
|
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");
|
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())
|
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_a = get_bool(cell->getPort("\\A"));
|
||||||
string name_en = get_bool(cell->getPort("\\EN"));
|
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)));
|
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 == "$cover")
|
||||||
if (cell->type == "$assert")
|
decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (and %s %s)) ; %s\n",
|
||||||
assert_list.push_back(stringf("(|%s#%d| state)", get_id(module), idcounter++));
|
get_id(module), postfix, id, get_id(module), name_a.c_str(), name_en.c_str(), get_id(cell)));
|
||||||
else
|
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++)
|
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_d = get_bool(cell->getPort("\\D"));
|
||||||
std::string expr_q = get_bool(cell->getPort("\\Q"), "next_state");
|
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"))));
|
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"))
|
if (cell->type.in("$ff", "$dff"))
|
||||||
|
@ -686,13 +968,16 @@ struct Smt2Worker
|
||||||
std::string expr_d = get_bv(cell->getPort("\\D"));
|
std::string expr_d = get_bv(cell->getPort("\\D"));
|
||||||
std::string expr_q = get_bv(cell->getPort("\\Q"), "next_state");
|
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"))));
|
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_d = get_bv(cell->getPort("\\Y"));
|
||||||
std::string expr_q = get_bv(cell->getPort("\\Y"), "next_state");
|
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"))));
|
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")
|
if (cell->type == "$mem")
|
||||||
|
@ -703,11 +988,93 @@ struct Smt2Worker
|
||||||
int width = cell->getParam("\\WIDTH").as_int();
|
int width = cell->getParam("\\WIDTH").as_int();
|
||||||
int wr_ports = cell->getParam("\\WR_PORTS").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++)
|
for (int i = 0; i < wr_ports; i++)
|
||||||
{
|
{
|
||||||
std::string addr = get_bv(cell->getPort("\\WR_ADDR").extract(abits*i, abits));
|
SigSpec addr_sig = cell->getPort("\\WR_ADDR").extract(abits*i, abits);
|
||||||
std::string data = get_bv(cell->getPort("\\WR_DATA").extract(width*i, width));
|
SigSpec data_sig = cell->getPort("\\WR_DATA").extract(width*i, width);
|
||||||
std::string mask = get_bv(cell->getPort("\\WR_EN").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 = 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());
|
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+1, get_id(module), abits, width,
|
||||||
get_id(module), arrayid, i, addr.c_str(), data.c_str(), get_id(cell)));
|
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_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);
|
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)));
|
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");
|
Const init_data = cell->getParam("\\INIT");
|
||||||
int memsize = cell->getParam("\\SIZE").as_int();
|
int memsize = cell->getParam("\\SIZE").as_int();
|
||||||
|
@ -737,7 +1109,11 @@ struct Smt2Worker
|
||||||
if (bit == State::S0 || bit == State::S1)
|
if (bit == State::S0 || bit == State::S1)
|
||||||
gen_init_constr = true;
|
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]",
|
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(),
|
get_id(module), arrayid, Const(i, abits).as_string().c_str(),
|
||||||
initword.as_string().c_str(), get_id(cell), i));
|
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));
|
if (verbose) log("=> finalizing SMT2 representation of %s.\n", log_id(module));
|
||||||
|
|
||||||
for (auto c : hiercells) {
|
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)));
|
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",
|
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)));
|
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";
|
string assert_expr = assert_list.empty() ? "true" : "(and";
|
||||||
|
@ -832,6 +1209,18 @@ struct Smt2Worker
|
||||||
{
|
{
|
||||||
f << stringf("; yosys-smt2-module %s\n", get_id(module));
|
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)
|
for (auto it : decls)
|
||||||
f << it;
|
f << it;
|
||||||
|
|
||||||
|
@ -864,44 +1253,92 @@ struct Smt2Worker
|
||||||
|
|
||||||
struct Smt2Backend : public Backend {
|
struct Smt2Backend : public Backend {
|
||||||
Smt2Backend() : Backend("smt2", "write design to SMT-LIBv2 file") { }
|
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---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" write_smt2 [options] [filename]\n");
|
log(" write_smt2 [options] [filename]\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("Write a SMT-LIBv2 [1] description of the current design. For a module with name\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("'<mod>' this will declare the sort '<mod>_s' (state of the module) and will\n");
|
||||||
log("functions operating on that state.\n");
|
log("define and declare functions operating on that state.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("The '<mod>_s' sort represents a module state. Additional '<mod>_n' functions\n");
|
log("The following SMT2 functions are generated for a module with name '<mod>'.\n");
|
||||||
log("are provided that can be used to access the values of the signals in the module.\n");
|
log("Some declarations/definitions are printed with a special comment. A prover\n");
|
||||||
log("By default only ports, registers, and wires with the 'keep' attribute set are\n");
|
log("using the SMT2 files can use those comments to collect all relevant metadata\n");
|
||||||
log("made available via such functions. With the -nobv option, multi-bit wires are\n");
|
log("about the design.\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("\n");
|
log("\n");
|
||||||
log("The '<mod>_t' function evaluates to 'true' when the given pair of states\n");
|
log(" ; yosys-smt2-module <mod>\n");
|
||||||
log("describes a valid state transition.\n");
|
log(" (declare-sort |<mod>_s| 0)\n");
|
||||||
|
log(" The sort representing a state of module <mod>.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("The '<mod>_a' function evaluates to 'true' when the given state satisfies\n");
|
log(" (define-fun |<mod>_h| ((state |<mod>_s|)) Bool (...))\n");
|
||||||
log("the asserts in the module.\n");
|
log(" This function must be asserted for each state to establish the\n");
|
||||||
|
log(" design hierarchy.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("The '<mod>_u' function evaluates to 'true' when the given state satisfies\n");
|
log(" ; yosys-smt2-input <wirename> <width>\n");
|
||||||
log("the assumptions in the module.\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("\n");
|
||||||
log("The '<mod>_i' function evaluates to 'true' when the given state conforms\n");
|
log(" ; yosys-smt2-cell <submod> <instancename>\n");
|
||||||
log("to the initial state. Furthermore the '<mod>_is' function should be asserted\n");
|
log(" (declare-fun |<mod>_h <instancename>| (|<mod>_s|) |<submod>_s|)\n");
|
||||||
log("to be true for initial states in addition to '<mod>_i', and should be\n");
|
log(" There is a function like that for each hierarchical instance. It\n");
|
||||||
log("asserted to be false for non-initial states.\n");
|
log(" returns the sort that represents the state of the sub-module that\n");
|
||||||
|
log(" implements the instance.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log("For hierarchical designs, the '<mod>_h' function must be asserted for each\n");
|
log(" (declare-fun |<mod>_is| (|<mod>_s|) Bool)\n");
|
||||||
log("state to establish the design hierarchy. The '<mod>_h <cellname>' function\n");
|
log(" This function must be asserted 'true' for initial states, and 'false'\n");
|
||||||
log("evaluates to the state corresponding to the given cell within <mod>.\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("\n");
|
||||||
log(" -verbose\n");
|
log(" -verbose\n");
|
||||||
log(" this will print the recursive walk used to export the modules.\n");
|
log(" this will print the recursive walk used to export the modules.\n");
|
||||||
log("\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(" -nobv\n");
|
||||||
log(" disable support for BitVec (FixedSizeBitVectors theory). without this\n");
|
log(" disable support for BitVec (FixedSizeBitVectors theory). without this\n");
|
||||||
log(" option multi-bit wires are represented using the BitVec sort and\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("from non-zero to zero in the test design.\n");
|
||||||
log("\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;
|
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");
|
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");
|
log_warning("Options -bv and -mem are now the default. Support for -bv and -mem will be removed in the future.\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (args[argidx] == "-stbv") {
|
||||||
|
statebv = true;
|
||||||
|
statedt = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (args[argidx] == "-stdt") {
|
||||||
|
statebv = false;
|
||||||
|
statedt = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (args[argidx] == "-nobv") {
|
if (args[argidx] == "-nobv") {
|
||||||
bvmode = false;
|
bvmode = false;
|
||||||
memmode = false;
|
memmode = false;
|
||||||
|
@ -1033,6 +1481,12 @@ struct Smt2Backend : public Backend {
|
||||||
if (!memmode)
|
if (!memmode)
|
||||||
*f << stringf("; yosys-smt2-nomem\n");
|
*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;
|
std::vector<RTLIL::Module*> sorted_modules;
|
||||||
|
|
||||||
// extract module dependencies
|
// extract module dependencies
|
||||||
|
@ -1062,9 +1516,23 @@ struct Smt2Backend : public Backend {
|
||||||
module_deps.erase(sorted_modules.at(sorted_modules_idx++));
|
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();
|
Module *topmod = design->top_module();
|
||||||
std::string topmod_id;
|
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)
|
for (auto module : sorted_modules)
|
||||||
{
|
{
|
||||||
if (module->get_bool_attribute("\\blackbox") || module->has_memories_warn() || module->has_processes_warn())
|
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));
|
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.run();
|
||||||
worker.write(*f);
|
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
|
# yosys -- Yosys Open SYnthesis Suite
|
||||||
#
|
#
|
||||||
|
@ -17,10 +16,63 @@
|
||||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# 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 copy import deepcopy
|
||||||
from select import select
|
from select import select
|
||||||
from time import time
|
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 = {
|
hex_dict = {
|
||||||
|
@ -41,25 +93,38 @@ class SmtModInfo:
|
||||||
self.memories = dict()
|
self.memories = dict()
|
||||||
self.wires = set()
|
self.wires = set()
|
||||||
self.wsize = dict()
|
self.wsize = dict()
|
||||||
|
self.clocks = dict()
|
||||||
self.cells = dict()
|
self.cells = dict()
|
||||||
self.asserts = dict()
|
self.asserts = dict()
|
||||||
|
self.covers = dict()
|
||||||
self.anyconsts = dict()
|
self.anyconsts = dict()
|
||||||
|
self.anyseqs = dict()
|
||||||
|
self.allconsts = dict()
|
||||||
|
self.allseqs = dict()
|
||||||
|
self.asize = dict()
|
||||||
|
|
||||||
|
|
||||||
class SmtIo:
|
class SmtIo:
|
||||||
def __init__(self, opts=None):
|
def __init__(self, opts=None):
|
||||||
|
global solvers_index
|
||||||
|
|
||||||
self.logic = None
|
self.logic = None
|
||||||
self.logic_qf = True
|
self.logic_qf = True
|
||||||
self.logic_ax = True
|
self.logic_ax = True
|
||||||
self.logic_uf = True
|
self.logic_uf = True
|
||||||
self.logic_bv = True
|
self.logic_bv = True
|
||||||
|
self.logic_dt = False
|
||||||
|
self.forall = False
|
||||||
self.produce_models = True
|
self.produce_models = True
|
||||||
self.smt2cache = [list()]
|
self.smt2cache = [list()]
|
||||||
self.p = None
|
self.p = None
|
||||||
|
self.p_index = solvers_index
|
||||||
|
solvers_index += 1
|
||||||
|
|
||||||
if opts is not None:
|
if opts is not None:
|
||||||
self.logic = opts.logic
|
self.logic = opts.logic
|
||||||
self.solver = opts.solver
|
self.solver = opts.solver
|
||||||
|
self.solver_opts = opts.solver_opts
|
||||||
self.debug_print = opts.debug_print
|
self.debug_print = opts.debug_print
|
||||||
self.debug_file = opts.debug_file
|
self.debug_file = opts.debug_file
|
||||||
self.dummy_file = opts.dummy_file
|
self.dummy_file = opts.dummy_file
|
||||||
|
@ -70,33 +135,65 @@ class SmtIo:
|
||||||
self.nocomments = opts.nocomments
|
self.nocomments = opts.nocomments
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.solver = "z3"
|
self.solver = "yices"
|
||||||
|
self.solver_opts = list()
|
||||||
self.debug_print = False
|
self.debug_print = False
|
||||||
self.debug_file = None
|
self.debug_file = None
|
||||||
self.dummy_file = None
|
self.dummy_file = None
|
||||||
self.timeinfo = True
|
self.timeinfo = os.name != "nt"
|
||||||
self.unroll = False
|
self.unroll = False
|
||||||
self.noincr = False
|
self.noincr = False
|
||||||
self.info_stmts = list()
|
self.info_stmts = list()
|
||||||
self.nocomments = False
|
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":
|
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":
|
if self.solver == "z3":
|
||||||
self.popen_vargs = ['z3', '-smt2', '-in']
|
self.popen_vargs = ['z3', '-smt2', '-in'] + self.solver_opts
|
||||||
|
|
||||||
if self.solver == "cvc4":
|
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":
|
if self.solver == "mathsat":
|
||||||
self.popen_vargs = ['mathsat']
|
self.popen_vargs = ['mathsat'] + self.solver_opts
|
||||||
|
|
||||||
if self.solver == "boolector":
|
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
|
self.unroll = True
|
||||||
|
|
||||||
if self.solver == "abc":
|
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.popen_vargs = ['yosys-abc', '-S', '%blast; &sweep -C 5000; &syn4; &cec -s -m -C 2000']
|
||||||
self.logic_ax = False
|
self.logic_ax = False
|
||||||
self.unroll = True
|
self.unroll = True
|
||||||
|
@ -109,9 +206,10 @@ class SmtIo:
|
||||||
if self.dummy_file is not None:
|
if self.dummy_file is not None:
|
||||||
self.dummy_fd = open(self.dummy_file, "w")
|
self.dummy_fd = open(self.dummy_file, "w")
|
||||||
if not self.noincr:
|
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:
|
if self.unroll:
|
||||||
|
assert not self.forall
|
||||||
self.logic_uf = False
|
self.logic_uf = False
|
||||||
self.unroll_idcnt = 0
|
self.unroll_idcnt = 0
|
||||||
self.unroll_buffer = ""
|
self.unroll_buffer = ""
|
||||||
|
@ -121,36 +219,27 @@ class SmtIo:
|
||||||
self.unroll_cache = dict()
|
self.unroll_cache = dict()
|
||||||
self.unroll_stack = list()
|
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:
|
if self.logic is None:
|
||||||
self.logic = ""
|
self.logic = ""
|
||||||
if self.logic_qf: self.logic += "QF_"
|
if self.logic_qf: self.logic += "QF_"
|
||||||
if self.logic_ax: self.logic += "A"
|
if self.logic_ax: self.logic += "A"
|
||||||
if self.logic_uf: self.logic += "UF"
|
if self.logic_uf: self.logic += "UF"
|
||||||
if self.logic_bv: self.logic += "BV"
|
if self.logic_bv: self.logic += "BV"
|
||||||
|
if self.logic_dt: self.logic = "ALL"
|
||||||
|
|
||||||
self.setup_done = True
|
self.setup_done = True
|
||||||
|
|
||||||
|
for stmt in self.info_stmts:
|
||||||
|
self.write(stmt)
|
||||||
|
|
||||||
if self.produce_models:
|
if self.produce_models:
|
||||||
self.write("(set-option :produce-models true)")
|
self.write("(set-option :produce-models true)")
|
||||||
|
|
||||||
self.write("(set-logic %s)" % self.logic)
|
self.write("(set-logic %s)" % self.logic)
|
||||||
|
|
||||||
for stmt in self.info_stmts:
|
|
||||||
self.write(stmt)
|
|
||||||
|
|
||||||
def timestamp(self):
|
def timestamp(self):
|
||||||
secs = int(time() - self.start_time)
|
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):
|
def replace_in_stmt(self, stmt, pat, repl):
|
||||||
if stmt == pat:
|
if stmt == pat:
|
||||||
|
@ -201,18 +290,75 @@ class SmtIo:
|
||||||
|
|
||||||
return stmt
|
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):
|
def write(self, stmt, unroll=True):
|
||||||
if stmt.startswith(";"):
|
if stmt.startswith(";"):
|
||||||
self.info(stmt)
|
self.info(stmt)
|
||||||
|
if not self.setup_done:
|
||||||
|
self.info_stmts.append(stmt)
|
||||||
|
return
|
||||||
elif not self.setup_done:
|
elif not self.setup_done:
|
||||||
self.setup()
|
self.setup()
|
||||||
|
|
||||||
stmt = stmt.strip()
|
stmt = stmt.strip()
|
||||||
|
|
||||||
if self.nocomments or self.unroll:
|
if self.nocomments or self.unroll:
|
||||||
if stmt.startswith(";"):
|
stmt = re.sub(r" *;.*", "", stmt)
|
||||||
return
|
if stmt == "": return
|
||||||
stmt = re.sub(r" ;.*", "", stmt)
|
|
||||||
|
|
||||||
if unroll and self.unroll:
|
if unroll and self.unroll:
|
||||||
stmt = self.unroll_buffer + stmt
|
stmt = self.unroll_buffer + stmt
|
||||||
|
@ -271,20 +417,17 @@ class SmtIo:
|
||||||
if self.solver != "dummy":
|
if self.solver != "dummy":
|
||||||
if self.noincr:
|
if self.noincr:
|
||||||
if self.p is not None and not stmt.startswith("(get-"):
|
if self.p is not None and not stmt.startswith("(get-"):
|
||||||
self.p.stdin.close()
|
self.p_close()
|
||||||
self.p = None
|
|
||||||
if stmt == "(push 1)":
|
if stmt == "(push 1)":
|
||||||
self.smt2cache.append(list())
|
self.smt2cache.append(list())
|
||||||
elif stmt == "(pop 1)":
|
elif stmt == "(pop 1)":
|
||||||
self.smt2cache.pop()
|
self.smt2cache.pop()
|
||||||
else:
|
else:
|
||||||
if self.p is not None:
|
if self.p is not None:
|
||||||
self.p.stdin.write(bytes(stmt + "\n", "ascii"))
|
self.p_write(stmt + "\n", True)
|
||||||
self.p.stdin.flush()
|
|
||||||
self.smt2cache[-1].append(stmt)
|
self.smt2cache[-1].append(stmt)
|
||||||
else:
|
else:
|
||||||
self.p.stdin.write(bytes(stmt + "\n", "ascii"))
|
self.p_write(stmt + "\n", True)
|
||||||
self.p.stdin.flush()
|
|
||||||
|
|
||||||
def info(self, stmt):
|
def info(self, stmt):
|
||||||
if not stmt.startswith("; yosys-smt2-"):
|
if not stmt.startswith("; yosys-smt2-"):
|
||||||
|
@ -300,6 +443,15 @@ class SmtIo:
|
||||||
if self.logic is None:
|
if self.logic is None:
|
||||||
self.logic_bv = False
|
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":
|
if fields[1] == "yosys-smt2-module":
|
||||||
self.curmod = fields[2]
|
self.curmod = fields[2]
|
||||||
self.modinfo[self.curmod] = SmtModInfo()
|
self.modinfo[self.curmod] = SmtModInfo()
|
||||||
|
@ -323,17 +475,40 @@ class SmtIo:
|
||||||
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
|
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
|
||||||
|
|
||||||
if fields[1] == "yosys-smt2-memory":
|
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":
|
if fields[1] == "yosys-smt2-wire":
|
||||||
self.modinfo[self.curmod].wires.add(fields[2])
|
self.modinfo[self.curmod].wires.add(fields[2])
|
||||||
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
|
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":
|
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":
|
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(self, top, regs_only=False):
|
||||||
def hiernets_worker(nets, mod, cursor):
|
def hiernets_worker(nets, mod, cursor):
|
||||||
|
@ -347,6 +522,54 @@ class SmtIo:
|
||||||
hiernets_worker(nets, top, [])
|
hiernets_worker(nets, top, [])
|
||||||
return nets
|
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(self, top):
|
||||||
def hiermems_worker(mems, mod, cursor):
|
def hiermems_worker(mems, mod, cursor):
|
||||||
for memname in sorted(self.modinfo[mod].memories.keys()):
|
for memname in sorted(self.modinfo[mod].memories.keys()):
|
||||||
|
@ -366,7 +589,7 @@ class SmtIo:
|
||||||
if self.solver == "dummy":
|
if self.solver == "dummy":
|
||||||
line = self.dummy_fd.readline().strip()
|
line = self.dummy_fd.readline().strip()
|
||||||
else:
|
else:
|
||||||
line = self.p.stdout.readline().decode("ascii").strip()
|
line = self.p_read().strip()
|
||||||
if self.dummy_file is not None:
|
if self.dummy_file is not None:
|
||||||
self.dummy_fd.write(line + "\n")
|
self.dummy_fd.write(line + "\n")
|
||||||
|
|
||||||
|
@ -379,12 +602,14 @@ class SmtIo:
|
||||||
if count_brackets == 0:
|
if count_brackets == 0:
|
||||||
break
|
break
|
||||||
if self.solver != "dummy" and self.p.poll():
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
stmt = "".join(stmt)
|
stmt = "".join(stmt)
|
||||||
if stmt.startswith("(error"):
|
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)
|
sys.exit(1)
|
||||||
|
|
||||||
return stmt
|
return stmt
|
||||||
|
@ -399,15 +624,13 @@ class SmtIo:
|
||||||
if self.solver != "dummy":
|
if self.solver != "dummy":
|
||||||
if self.noincr:
|
if self.noincr:
|
||||||
if self.p is not None:
|
if self.p is not None:
|
||||||
self.p.stdin.close()
|
self.p_close()
|
||||||
self.p = None
|
self.p_open()
|
||||||
self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
||||||
for cache_ctx in self.smt2cache:
|
for cache_ctx in self.smt2cache:
|
||||||
for cache_stmt in cache_ctx:
|
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_write("(check-sat)\n", True)
|
||||||
self.p.stdin.flush()
|
|
||||||
|
|
||||||
if self.timeinfo:
|
if self.timeinfo:
|
||||||
i = 0
|
i = 0
|
||||||
|
@ -415,7 +638,7 @@ class SmtIo:
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
num_bs = 0
|
num_bs = 0
|
||||||
while select([self.p.stdout], [], [], 0.1) == ([], [], []):
|
while self.p_poll():
|
||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
if count < 25:
|
if count < 25:
|
||||||
|
@ -444,11 +667,43 @@ class SmtIo:
|
||||||
print("\b \b" * num_bs, end="", file=sys.stderr)
|
print("\b \b" * num_bs, end="", file=sys.stderr)
|
||||||
sys.stderr.flush()
|
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()
|
result = self.read()
|
||||||
|
|
||||||
if self.debug_file:
|
if self.debug_file:
|
||||||
print("(set-info :status %s)" % result, file=self.debug_file)
|
print("(set-info :status %s)" % result, file=self.debug_file)
|
||||||
print("(check-sat)", file=self.debug_file)
|
print("(check-sat)", file=self.debug_file)
|
||||||
self.debug_file.flush()
|
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
|
return result
|
||||||
|
|
||||||
def parse(self, stmt):
|
def parse(self, stmt):
|
||||||
|
@ -503,6 +758,9 @@ class SmtIo:
|
||||||
return h
|
return h
|
||||||
|
|
||||||
def bv2bin(self, v):
|
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 == "true": return "1"
|
||||||
if v == "false": return "0"
|
if v == "false": return "0"
|
||||||
if v.startswith("#b"):
|
if v.startswith("#b"):
|
||||||
|
@ -539,6 +797,9 @@ class SmtIo:
|
||||||
return [".".join(path)]
|
return [".".join(path)]
|
||||||
|
|
||||||
def net_expr(self, mod, base, path):
|
def net_expr(self, mod, base, path):
|
||||||
|
if len(path) == 0:
|
||||||
|
return base
|
||||||
|
|
||||||
if len(path) == 1:
|
if len(path) == 1:
|
||||||
assert mod in self.modinfo
|
assert mod in self.modinfo
|
||||||
if path[0] == "":
|
if path[0] == "":
|
||||||
|
@ -568,23 +829,54 @@ class SmtIo:
|
||||||
assert net_path[-1] in self.modinfo[mod].wsize
|
assert net_path[-1] in self.modinfo[mod].wsize
|
||||||
return self.modinfo[mod].wsize[net_path[-1]]
|
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:
|
if len(path) == 1:
|
||||||
assert mod in self.modinfo
|
assert mod in self.modinfo
|
||||||
assert path[0] in self.modinfo[mod].memories
|
assert path[0] in self.modinfo[mod].memories
|
||||||
if infomode:
|
if infomode:
|
||||||
return self.modinfo[mod].memories[path[0]]
|
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 mod in self.modinfo
|
||||||
assert path[0] in self.modinfo[mod].cells
|
assert path[0] in self.modinfo[mod].cells
|
||||||
|
|
||||||
nextmod = self.modinfo[mod].cells[path[0]]
|
nextmod = self.modinfo[mod].cells[path[0]]
|
||||||
nextbase = "(|%s_h %s| %s)" % (mod, path[0], base)
|
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):
|
def mem_info(self, mod, path):
|
||||||
return self.mem_expr(mod, base, path, infomode=True)
|
return self.mem_expr(mod, "", path, infomode=True)
|
||||||
|
|
||||||
def get_net(self, mod_name, net_path, state_name):
|
def get_net(self, mod_name, net_path, state_name):
|
||||||
return self.get(self.net_expr(mod_name, state_name, net_path))
|
return self.get(self.net_expr(mod_name, state_name, net_path))
|
||||||
|
@ -607,19 +899,21 @@ class SmtIo:
|
||||||
def wait(self):
|
def wait(self):
|
||||||
if self.p is not None:
|
if self.p is not None:
|
||||||
self.p.wait()
|
self.p.wait()
|
||||||
|
self.p_close()
|
||||||
|
|
||||||
|
|
||||||
class SmtOpts:
|
class SmtOpts:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.shortopts = "s:v"
|
self.shortopts = "s:S:v"
|
||||||
self.longopts = ["unroll", "noincr", "noprogress", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"]
|
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_print = False
|
||||||
self.debug_file = None
|
self.debug_file = None
|
||||||
self.dummy_file = None
|
self.dummy_file = None
|
||||||
self.unroll = False
|
self.unroll = False
|
||||||
self.noincr = False
|
self.noincr = False
|
||||||
self.timeinfo = True
|
self.timeinfo = os.name != "nt"
|
||||||
self.logic = None
|
self.logic = None
|
||||||
self.info_stmts = list()
|
self.info_stmts = list()
|
||||||
self.nocomments = False
|
self.nocomments = False
|
||||||
|
@ -627,6 +921,8 @@ class SmtOpts:
|
||||||
def handle(self, o, a):
|
def handle(self, o, a):
|
||||||
if o == "-s":
|
if o == "-s":
|
||||||
self.solver = a
|
self.solver = a
|
||||||
|
elif o == "-S":
|
||||||
|
self.solver_opts.append(a)
|
||||||
elif o == "-v":
|
elif o == "-v":
|
||||||
self.debug_print = True
|
self.debug_print = True
|
||||||
elif o == "--unroll":
|
elif o == "--unroll":
|
||||||
|
@ -634,7 +930,7 @@ class SmtOpts:
|
||||||
elif o == "--noincr":
|
elif o == "--noincr":
|
||||||
self.noincr = True
|
self.noincr = True
|
||||||
elif o == "--noprogress":
|
elif o == "--noprogress":
|
||||||
self.timeinfo = True
|
self.timeinfo = False
|
||||||
elif o == "--dump-smt2":
|
elif o == "--dump-smt2":
|
||||||
self.debug_file = open(a, "w")
|
self.debug_file = open(a, "w")
|
||||||
elif o == "--logic":
|
elif o == "--logic":
|
||||||
|
@ -652,8 +948,11 @@ class SmtOpts:
|
||||||
def helpmsg(self):
|
def helpmsg(self):
|
||||||
return """
|
return """
|
||||||
-s <solver>
|
-s <solver>
|
||||||
set SMT solver: z3, cvc4, yices, mathsat, boolector, dummy
|
set SMT solver: z3, yices, boolector, cvc4, mathsat, dummy
|
||||||
default: z3
|
default: yices
|
||||||
|
|
||||||
|
-S <opt>
|
||||||
|
pass <opt> as command line argument to the solver
|
||||||
|
|
||||||
--logic <smt2_logic>
|
--logic <smt2_logic>
|
||||||
use the specified SMT2 logic (e.g. QF_AUFBV)
|
use the specified SMT2 logic (e.g. QF_AUFBV)
|
||||||
|
@ -674,6 +973,7 @@ class SmtOpts:
|
||||||
|
|
||||||
--noprogress
|
--noprogress
|
||||||
disable timer display during solving
|
disable timer display during solving
|
||||||
|
(this option is set implicitly on Windows)
|
||||||
|
|
||||||
--dump-smt2 <filename>
|
--dump-smt2 <filename>
|
||||||
write smt2 statements to file
|
write smt2 statements to file
|
||||||
|
@ -691,6 +991,7 @@ class MkVcd:
|
||||||
self.f = f
|
self.f = f
|
||||||
self.t = -1
|
self.t = -1
|
||||||
self.nets = dict()
|
self.nets = dict()
|
||||||
|
self.clocks = dict()
|
||||||
|
|
||||||
def add_net(self, path, width):
|
def add_net(self, path, width):
|
||||||
path = tuple(path)
|
path = tuple(path)
|
||||||
|
@ -698,34 +999,78 @@ class MkVcd:
|
||||||
key = "n%d" % len(self.nets)
|
key = "n%d" % len(self.nets)
|
||||||
self.nets[path] = (key, width)
|
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):
|
def set_net(self, path, bits):
|
||||||
path = tuple(path)
|
path = tuple(path)
|
||||||
assert self.t >= 0
|
assert self.t >= 0
|
||||||
assert path in self.nets
|
assert path in self.nets
|
||||||
|
if path not in self.clocks:
|
||||||
print("b%s %s" % (bits, self.nets[path][0]), file=self.f)
|
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):
|
def set_time(self, t):
|
||||||
assert t >= self.t
|
assert t >= self.t
|
||||||
if t != self.t:
|
if t != self.t:
|
||||||
if self.t == -1:
|
if self.t == -1:
|
||||||
print("$var integer 32 t smt_step $end", file=self.f)
|
print("$var integer 32 t smt_step $end", file=self.f)
|
||||||
print("$var event 1 ! smt_clock $end", file=self.f)
|
print("$var event 1 ! smt_clock $end", file=self.f)
|
||||||
|
|
||||||
scope = []
|
scope = []
|
||||||
for path in sorted(self.nets):
|
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)
|
print("$upscope $end", file=self.f)
|
||||||
scope = scope[:-1]
|
scope = scope[:-1]
|
||||||
while len(scope)+1 < len(path):
|
|
||||||
print("$scope module %s $end" % path[len(scope)], file=self.f)
|
while uipath[:-1] != scope:
|
||||||
scope.append(path[len(scope)-1])
|
print("$scope module %s $end" % uipath[len(scope)], file=self.f)
|
||||||
key, width = self.nets[path]
|
scope.append(uipath[len(scope)])
|
||||||
print("$var wire %d %s %s $end" % (width, key, path[-1]), file=self.f)
|
|
||||||
|
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)):
|
for i in range(len(scope)):
|
||||||
print("$upscope $end", file=self.f)
|
print("$upscope $end", file=self.f)
|
||||||
|
|
||||||
print("$enddefinitions $end", file=self.f)
|
print("$enddefinitions $end", file=self.f)
|
||||||
|
|
||||||
self.t = t
|
self.t = t
|
||||||
assert self.t >= 0
|
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("#%d" % (10 * self.t), file=self.f)
|
||||||
print("1!", file=self.f)
|
print("1!", file=self.f)
|
||||||
print("b%s t" % format(self.t, "032b"), 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;
|
pool<Wire*> partial_assignment_wires;
|
||||||
dict<SigBit, std::pair<const char*, int>> partial_assignment_bits;
|
dict<SigBit, std::pair<const char*, int>> partial_assignment_bits;
|
||||||
vector<string> assignments, invarspecs;
|
vector<string> inputvars, vars, definitions, assignments, invarspecs;
|
||||||
|
|
||||||
const char *cid()
|
const char *cid()
|
||||||
{
|
{
|
||||||
|
@ -195,7 +195,7 @@ struct SmvWorker
|
||||||
return rvalue(sig);
|
return rvalue(sig);
|
||||||
|
|
||||||
const char *temp_id = cid();
|
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;
|
int offset = 0;
|
||||||
for (auto bit : sig) {
|
for (auto bit : sig) {
|
||||||
|
@ -210,14 +210,14 @@ struct SmvWorker
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
f << stringf("MODULE %s\n", cid(module->name));
|
f << stringf("MODULE %s\n", cid(module->name));
|
||||||
f << stringf(" VAR\n");
|
|
||||||
|
|
||||||
for (auto wire : module->wires())
|
for (auto wire : module->wires())
|
||||||
{
|
{
|
||||||
if (SigSpec(wire) != sigmap(wire))
|
if (SigSpec(wire) != sigmap(wire))
|
||||||
partial_assignment_wires.insert(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"))
|
if (wire->attributes.count("\\init"))
|
||||||
assignments.push_back(stringf("init(%s) := %s;", lvalue(wire), rvalue(wire->attributes.at("\\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_shr = rvalue_u(sig_b);
|
||||||
const char *b_shl = cid();
|
const char *b_shl = cid();
|
||||||
|
|
||||||
f << stringf(" %s : unsigned word[%d]; -- neg(%s)\n", b_shl, GetSize(sig_b), log_signal(sig_b));
|
// 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)));
|
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_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);
|
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());
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -319,12 +319,12 @@ struct SmvWorker
|
||||||
|
|
||||||
if (cell->getParam("\\A_SIGNED").as_bool())
|
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)));
|
op.c_str(), rvalue_s(cell->getPort("\\A"), width)));
|
||||||
}
|
}
|
||||||
else
|
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)));
|
op.c_str(), rvalue_u(cell->getPort("\\A"), width)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,12 +346,12 @@ struct SmvWorker
|
||||||
|
|
||||||
if (cell->getParam("\\A_SIGNED").as_bool())
|
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)));
|
rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width)));
|
||||||
}
|
}
|
||||||
else
|
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)));
|
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())
|
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));
|
rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width), width_y));
|
||||||
}
|
}
|
||||||
else
|
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));
|
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);
|
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"))));
|
expr_a.c_str(), op.c_str(), expr_b.c_str(), GetSize(cell->getPort("\\Y"))));
|
||||||
|
|
||||||
continue;
|
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_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);
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +444,7 @@ struct SmvWorker
|
||||||
if (cell->type == "$reduce_xnor")
|
if (cell->type == "$reduce_xnor")
|
||||||
expr = "!(" + expr + ")";
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,7 +462,7 @@ struct SmvWorker
|
||||||
if (cell->type == "$logic_and") expr = expr_a + " & " + expr_b;
|
if (cell->type == "$logic_and") expr = expr_a + " & " + expr_b;
|
||||||
if (cell->type == "$logic_or") 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,7 +474,7 @@ struct SmvWorker
|
||||||
string expr_a = stringf("(%s = 0ub%d_0)", rvalue(cell->getPort("\\A")), width_a);
|
string expr_a = stringf("(%s = 0ub%d_0)", rvalue(cell->getPort("\\A")), width_a);
|
||||||
const char *expr_y = lvalue(cell->getPort("\\Y"));
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,12 +490,13 @@ struct SmvWorker
|
||||||
expr += stringf("bool(%s) ? %s : ", rvalue(sig_s[i]), rvalue(sig_b.extract(i*width, width)));
|
expr += stringf("bool(%s) ? %s : ", rvalue(sig_s[i]), rvalue(sig_b.extract(i*width, width)));
|
||||||
expr += rvalue(sig_a);
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->type == "$dff")
|
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"))));
|
assignments.push_back(stringf("next(%s) := %s;", lvalue(cell->getPort("\\Q")), rvalue(cell->getPort("\\D"))));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -503,59 +504,63 @@ struct SmvWorker
|
||||||
if (cell->type.in("$_BUF_", "$_NOT_"))
|
if (cell->type.in("$_BUF_", "$_NOT_"))
|
||||||
{
|
{
|
||||||
string op = cell->type == "$_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;
|
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;
|
string op;
|
||||||
|
|
||||||
if (cell->type.in("$_AND_", "$_NAND_")) op = "&";
|
if (cell->type.in("$_AND_", "$_NAND_", "$_ANDNOT_")) op = "&";
|
||||||
if (cell->type.in("$_OR_", "$_NOR_")) op = "|";
|
if (cell->type.in("$_OR_", "$_NOR_", "$_ORNOT_")) op = "|";
|
||||||
if (cell->type.in("$_XOR_")) op = "xor";
|
if (cell->type.in("$_XOR_")) op = "xor";
|
||||||
if (cell->type.in("$_XNOR_")) op = "xnor";
|
if (cell->type.in("$_XNOR_")) op = "xnor";
|
||||||
|
|
||||||
if (cell->type.in("$_NAND_", "$_NOR_"))
|
if (cell->type.in("$_ANDNOT_", "$_ORNOT_"))
|
||||||
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")), op.c_str(), rvalue(cell->getPort("\\B"))));
|
rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B"))));
|
||||||
else
|
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"))));
|
rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B"))));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->type == "$_MUX_")
|
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"))));
|
rvalue(cell->getPort("\\S")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\A"))));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->type == "$_AOI3_")
|
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"))));
|
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C"))));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->type == "$_OAI3_")
|
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"))));
|
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C"))));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->type == "$_AOI4_")
|
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"))));
|
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D"))));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell->type == "$_OAI4_")
|
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"))));
|
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D"))));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -563,13 +568,13 @@ struct SmvWorker
|
||||||
if (cell->type[0] == '$')
|
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));
|
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())
|
for (auto &conn : cell->connections())
|
||||||
if (cell->output(conn.first))
|
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
|
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)
|
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()) {
|
if (!assignments.empty()) {
|
||||||
|
@ -671,7 +694,7 @@ struct SmvWorker
|
||||||
|
|
||||||
struct SmvBackend : public Backend {
|
struct SmvBackend : public Backend {
|
||||||
SmvBackend() : Backend("smv", "write design to SMV file") { }
|
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---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -689,7 +712,7 @@ struct SmvBackend : public Backend {
|
||||||
log("THIS COMMAND IS UNDER CONSTRUCTION\n");
|
log("THIS COMMAND IS UNDER CONSTRUCTION\n");
|
||||||
log("\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;
|
std::ifstream template_f;
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
|
|
|
@ -132,7 +132,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De
|
||||||
|
|
||||||
struct SpiceBackend : public Backend {
|
struct SpiceBackend : public Backend {
|
||||||
SpiceBackend() : Backend("spice", "write design to SPICE netlist file") { }
|
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---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -161,7 +161,7 @@ struct SpiceBackend : public Backend {
|
||||||
log(" set the specified module as design top module\n");
|
log(" set the specified module as design top module\n");
|
||||||
log("\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 top_module_name;
|
||||||
RTLIL::Module *top_module = NULL;
|
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
|
USING_YOSYS_NAMESPACE
|
||||||
PRIVATE_NAMESPACE_BEGIN
|
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;
|
int auto_name_counter, auto_name_offset, auto_name_digits;
|
||||||
std::map<RTLIL::IdString, int> auto_name_map;
|
std::map<RTLIL::IdString, int> auto_name_map;
|
||||||
std::set<RTLIL::IdString> reg_wires, reg_ct;
|
std::set<RTLIL::IdString> reg_wires, reg_ct;
|
||||||
std::string auto_prefix;
|
std::string auto_prefix;
|
||||||
|
|
||||||
RTLIL::Module *active_module;
|
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)
|
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)
|
if (width < 0)
|
||||||
width = data.bits.size() - offset;
|
width = data.bits.size() - offset;
|
||||||
if (nostr)
|
if (nostr)
|
||||||
goto dump_bits;
|
goto dump_hex;
|
||||||
if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
|
if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
|
||||||
if (width == 32 && !no_decimal && !nodec) {
|
if (width == 32 && !no_decimal && !nodec) {
|
||||||
int32_t val = 0;
|
int32_t val = 0;
|
||||||
for (int i = offset+width-1; i >= offset; i--) {
|
for (int i = offset+width-1; i >= offset; i--) {
|
||||||
log_assert(i < (int)data.bits.size());
|
log_assert(i < (int)data.bits.size());
|
||||||
if (data.bits[i] != RTLIL::S0 && data.bits[i] != RTLIL::S1)
|
if (data.bits[i] != RTLIL::S0 && data.bits[i] != RTLIL::S1)
|
||||||
goto dump_bits;
|
goto dump_hex;
|
||||||
if (data.bits[i] == RTLIL::S1)
|
if (data.bits[i] == RTLIL::S1)
|
||||||
val |= 1 << (i - offset);
|
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);
|
f << stringf("-32'sd%u", -val);
|
||||||
else
|
else
|
||||||
f << stringf("32'%sd%u", set_signed ? "s" : "", val);
|
f << stringf("32'%sd%u", set_signed ? "s" : "", val);
|
||||||
} else {
|
} 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" : "");
|
f << stringf("%d'%sb", width, set_signed ? "s" : "");
|
||||||
if (width == 0)
|
if (width == 0)
|
||||||
f << stringf("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)
|
void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decimal = false)
|
||||||
{
|
{
|
||||||
if (chunk.wire == NULL) {
|
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)
|
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());
|
f << stringf("%s" "inout%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
|
||||||
if (reg_wires.count(wire->name)) {
|
if (reg_wires.count(wire->name)) {
|
||||||
f << stringf("%s" "reg%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
|
f << stringf("%s" "reg%s %s", indent.c_str(), range.c_str(), id(wire->name).c_str());
|
||||||
if (wire->attributes.count("\\init")) {
|
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"));
|
dump_const(f, wire->attributes.at("\\init"));
|
||||||
f << stringf(";\n");
|
|
||||||
}
|
}
|
||||||
|
f << stringf(";\n");
|
||||||
} else if (!wire->port_input && !wire->port_output)
|
} 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());
|
f << stringf("%s" "wire%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
|
||||||
#endif
|
#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)
|
void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory)
|
||||||
{
|
{
|
||||||
dump_attributes(f, indent, memory->attributes);
|
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)
|
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;
|
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());
|
f << stringf("%s" "assign ", indent.c_str());
|
||||||
dump_sigspec(f, cell->getPort("\\Y"));
|
dump_sigspec(f, cell->getPort("\\Y"));
|
||||||
f << stringf(" = ");
|
f << stringf(" = ");
|
||||||
|
@ -408,16 +480,18 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
||||||
f << stringf("~(");
|
f << stringf("~(");
|
||||||
dump_cell_expr_port(f, cell, "A", false);
|
dump_cell_expr_port(f, cell, "A", false);
|
||||||
f << stringf(" ");
|
f << stringf(" ");
|
||||||
if (cell->type.in("$_AND_", "$_NAND_"))
|
if (cell->type.in("$_AND_", "$_NAND_", "$_ANDNOT_"))
|
||||||
f << stringf("&");
|
f << stringf("&");
|
||||||
if (cell->type.in("$_OR_", "$_NOR_"))
|
if (cell->type.in("$_OR_", "$_NOR_", "$_ORNOT_"))
|
||||||
f << stringf("|");
|
f << stringf("|");
|
||||||
if (cell->type.in("$_XOR_", "$_XNOR_"))
|
if (cell->type.in("$_XOR_", "$_XNOR_"))
|
||||||
f << stringf("^");
|
f << stringf("^");
|
||||||
dump_attributes(f, "", cell->attributes, ' ');
|
dump_attributes(f, "", cell->attributes, ' ');
|
||||||
f << stringf(" ");
|
f << stringf(" ");
|
||||||
|
if (cell->type.in("$_ANDNOT_", "$_ORNOT_"))
|
||||||
|
f << stringf("~(");
|
||||||
dump_cell_expr_port(f, cell, "B", false);
|
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(")");
|
||||||
f << stringf(";\n");
|
f << stringf(";\n");
|
||||||
return true;
|
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);
|
std::string reg_name = cellname(cell);
|
||||||
bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
|
bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
|
||||||
|
|
||||||
if (!out_is_reg_wire)
|
if (!out_is_reg_wire) {
|
||||||
f << stringf("%s" "reg %s;\n", indent.c_str(), reg_name.c_str());
|
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);
|
dump_attributes(f, indent, cell->attributes);
|
||||||
f << stringf("%s" "always @(%sedge ", indent.c_str(), cell->type[6] == 'P' ? "pos" : "neg");
|
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);
|
std::string reg_name = cellname(cell);
|
||||||
bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
|
bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
|
||||||
|
|
||||||
if (!out_is_reg_wire)
|
if (!out_is_reg_wire) {
|
||||||
f << stringf("%s" "reg %s;\n", indent.c_str(), reg_name.c_str());
|
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);
|
dump_attributes(f, indent, cell->attributes);
|
||||||
f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_c == 'P' ? "pos" : "neg");
|
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_UNIOP
|
||||||
#undef HANDLE_BINOP
|
#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")
|
if (cell->type == "$mux")
|
||||||
{
|
{
|
||||||
f << stringf("%s" "assign ", indent.c_str());
|
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;
|
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")
|
if (cell->type == "$dffsr")
|
||||||
{
|
{
|
||||||
SigSpec sig_clk = cell->getPort("\\CLK");
|
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);
|
std::string reg_name = cellname(cell);
|
||||||
bool out_is_reg_wire = is_reg_wire(sig_q, reg_name);
|
bool out_is_reg_wire = is_reg_wire(sig_q, reg_name);
|
||||||
|
|
||||||
if (!out_is_reg_wire)
|
if (!out_is_reg_wire) {
|
||||||
f << stringf("%s" "reg [%d:0] %s;\n", indent.c_str(), width-1, reg_name.c_str());
|
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++) {
|
for (int i = 0; i < width; i++) {
|
||||||
f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
|
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);
|
std::string reg_name = cellname(cell);
|
||||||
bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
|
bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
|
||||||
|
|
||||||
if (!out_is_reg_wire)
|
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());
|
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");
|
f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
|
||||||
dump_sigspec(f, sig_clk);
|
dump_sigspec(f, sig_clk);
|
||||||
|
@ -794,27 +939,64 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
|
||||||
return true;
|
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")
|
if (cell->type == "$mem")
|
||||||
{
|
{
|
||||||
RTLIL::IdString memid = cell->parameters["\\MEMID"].decode_string();
|
RTLIL::IdString memid = cell->parameters["\\MEMID"].decode_string();
|
||||||
std::string mem_id = id(cell->parameters["\\MEMID"].decode_string());
|
std::string mem_id = id(cell->parameters["\\MEMID"].decode_string());
|
||||||
int abits = cell->parameters["\\ABITS"].as_int();
|
int abits = cell->parameters["\\ABITS"].as_int();
|
||||||
int size = cell->parameters["\\SIZE"].as_int();
|
int size = cell->parameters["\\SIZE"].as_int();
|
||||||
|
int offset = cell->parameters["\\OFFSET"].as_int();
|
||||||
int width = cell->parameters["\\WIDTH"].as_int();
|
int width = cell->parameters["\\WIDTH"].as_int();
|
||||||
bool use_init = !(RTLIL::SigSpec(cell->parameters["\\INIT"]).is_fully_undef());
|
bool use_init = !(RTLIL::SigSpec(cell->parameters["\\INIT"]).is_fully_undef());
|
||||||
|
|
||||||
// for memory block make something like:
|
// for memory block make something like:
|
||||||
// reg [7:0] memid [3:0];
|
// reg [7:0] memid [3:0];
|
||||||
// initial begin
|
// initial begin
|
||||||
// memid[0] <= ...
|
// memid[0] = ...
|
||||||
// end
|
// 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)
|
if (use_init)
|
||||||
{
|
{
|
||||||
f << stringf("%s" "initial begin\n", indent.c_str());
|
f << stringf("%s" "initial begin\n", indent.c_str());
|
||||||
for (int i=0; i<size; i++)
|
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));
|
dump_const(f, cell->parameters["\\INIT"].extract(i*width, width));
|
||||||
f << stringf(";\n");
|
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();
|
int nwrite_ports = cell->parameters["\\WR_PORTS"].as_int();
|
||||||
RTLIL::SigSpec sig_wr_clk, sig_wr_data, sig_wr_addr, sig_wr_en;
|
RTLIL::SigSpec sig_wr_clk, sig_wr_data, sig_wr_addr, sig_wr_en;
|
||||||
bool wr_clk_posedge;
|
bool wr_clk_posedge;
|
||||||
SigMap sigmap(active_module);
|
|
||||||
// write ports
|
// write ports
|
||||||
for (int i=0; i < nwrite_ports; i++)
|
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;
|
int start_i = i, width = 1;
|
||||||
SigBit wen_bit = sig_wr_en[i];
|
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++;
|
i++, width++;
|
||||||
|
|
||||||
if (wen_bit == State::S0)
|
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) {
|
if (sync->type == RTLIL::STa) {
|
||||||
f << stringf("%s" "always @* begin\n", indent.c_str());
|
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 {
|
} else {
|
||||||
f << stringf("%s" "always @(", indent.c_str());
|
f << stringf("%s" "always @(", indent.c_str());
|
||||||
if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1)
|
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();
|
reg_wires.clear();
|
||||||
reset_auto_counter(module);
|
reset_auto_counter(module);
|
||||||
active_module = 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())
|
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"
|
"can't always be mapped directly to Verilog always blocks. Unintended\n"
|
||||||
"changes in simulation behavior are possible! Use \"proc\" to convert\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");
|
f << stringf("\n");
|
||||||
for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
|
for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
|
||||||
|
@ -1327,11 +1521,13 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
|
||||||
|
|
||||||
f << stringf("%s" "endmodule\n", indent.c_str());
|
f << stringf("%s" "endmodule\n", indent.c_str());
|
||||||
active_module = NULL;
|
active_module = NULL;
|
||||||
|
active_sigmap.clear();
|
||||||
|
active_initdata.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VerilogBackend : public Backend {
|
struct VerilogBackend : public Backend {
|
||||||
VerilogBackend() : Backend("verilog", "write design to Verilog file") { }
|
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---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -1359,13 +1555,21 @@ struct VerilogBackend : public Backend {
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -nodec\n");
|
log(" -nodec\n");
|
||||||
log(" 32-bit constant values are by default dumped as decimal numbers,\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(" will write out all constants in binary.\n");
|
||||||
log("\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(" -nostr\n");
|
||||||
log(" Parameters and attributes that are specified as strings in the\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(" 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(" as binary numbers.\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -defparam\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("this command is called on a design with RTLIL processes.\n");
|
||||||
log("\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");
|
log_header(design, "Executing Verilog backend.\n");
|
||||||
|
|
||||||
|
@ -1401,8 +1605,10 @@ struct VerilogBackend : public Backend {
|
||||||
attr2comment = false;
|
attr2comment = false;
|
||||||
noexpr = false;
|
noexpr = false;
|
||||||
nodec = false;
|
nodec = false;
|
||||||
|
nohex = false;
|
||||||
nostr = false;
|
nostr = false;
|
||||||
defparam = false;
|
defparam = false;
|
||||||
|
decimal = false;
|
||||||
auto_prefix = "";
|
auto_prefix = "";
|
||||||
|
|
||||||
bool blackboxes = false;
|
bool blackboxes = false;
|
||||||
|
@ -1412,6 +1618,8 @@ struct VerilogBackend : public Backend {
|
||||||
|
|
||||||
reg_ct.insert("$dff");
|
reg_ct.insert("$dff");
|
||||||
reg_ct.insert("$adff");
|
reg_ct.insert("$adff");
|
||||||
|
reg_ct.insert("$dffe");
|
||||||
|
reg_ct.insert("$dlatch");
|
||||||
|
|
||||||
reg_ct.insert("$_DFF_N_");
|
reg_ct.insert("$_DFF_N_");
|
||||||
reg_ct.insert("$_DFF_P_");
|
reg_ct.insert("$_DFF_P_");
|
||||||
|
@ -1461,6 +1669,10 @@ struct VerilogBackend : public Backend {
|
||||||
nodec = true;
|
nodec = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (arg == "-nohex") {
|
||||||
|
nohex = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (arg == "-nostr") {
|
if (arg == "-nostr") {
|
||||||
nostr = true;
|
nostr = true;
|
||||||
continue;
|
continue;
|
||||||
|
@ -1469,6 +1681,10 @@ struct VerilogBackend : public Backend {
|
||||||
defparam = true;
|
defparam = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (arg == "-decimal") {
|
||||||
|
decimal = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (arg == "-blackboxes") {
|
if (arg == "-blackboxes") {
|
||||||
blackboxes = true;
|
blackboxes = true;
|
||||||
continue;
|
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]
|
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
|
connect_hw_server
|
||||||
open_hw_target [lindex [get_hw_targets] 0]
|
open_hw_target [lindex [get_hw_targets] 0]
|
||||||
set_property PROGRAM.FILE example.bit [lindex [get_hw_devices] 0]
|
set_property PROGRAM.FILE example.bit [lindex [get_hw_devices] 0]
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
|
|
||||||
read_verilog counter.v
|
read_verilog counter.v
|
||||||
read_verilog -lib cmos_cells.v
|
read_verilog -lib cmos_cells.v
|
||||||
|
|
||||||
proc;; memory;; techmap;;
|
synth
|
||||||
|
|
||||||
dfflibmap -liberty cmos_cells.lib
|
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
|
# http://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/cadence/lib/tsmc025/signalstorm/osu025_stdcells.lib
|
||||||
# dfflibmap -liberty osu025_stdcells.lib
|
# dfflibmap -liberty osu025_stdcells.lib
|
||||||
|
@ -13,4 +14,3 @@ abc -liberty cmos_cells.lib;;
|
||||||
|
|
||||||
write_verilog synth.v
|
write_verilog synth.v
|
||||||
write_spice synth.sp
|
write_spice synth.sp
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ struct EvalDemoPass : public Pass
|
||||||
{
|
{
|
||||||
EvalDemoPass() : Pass("evaldemo") { }
|
EvalDemoPass() : Pass("evaldemo") { }
|
||||||
|
|
||||||
virtual void execute(vector<string>, Design *design)
|
void execute(vector<string>, Design *design) YS_OVERRIDE
|
||||||
{
|
{
|
||||||
Module *module = design->top_module();
|
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
|
demo6.yslog
|
||||||
demo7.smt2
|
demo7.smt2
|
||||||
demo7.yslog
|
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
|
demo1: demo1.smt2
|
||||||
yosys-smtbmc --dump-vcd demo1.vcd demo1.smt2
|
yosys-smtbmc --dump-vcd demo1.vcd demo1.smt2
|
||||||
|
@ -25,6 +25,9 @@ demo6: demo6.smt2
|
||||||
demo7: demo7.smt2
|
demo7: demo7.smt2
|
||||||
yosys-smtbmc -t 10 demo7.smt2
|
yosys-smtbmc -t 10 demo7.smt2
|
||||||
|
|
||||||
|
demo8: demo8.smt2
|
||||||
|
yosys-smtbmc -s z3 -t 1 -g demo8.smt2
|
||||||
|
|
||||||
demo1.smt2: demo1.v
|
demo1.smt2: demo1.v
|
||||||
yosys -ql demo1.yslog -p 'read_verilog -formal demo1.v; prep -top demo1 -nordff; write_smt2 -wires demo1.smt2'
|
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
|
demo7.smt2: demo7.v
|
||||||
yosys -ql demo7.yslog -p 'read_verilog -formal demo7.v; prep -top demo7 -nordff; write_smt2 -wires demo7.smt2'
|
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:
|
clean:
|
||||||
rm -f demo1.yslog demo1.smt2 demo1.vcd
|
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
|
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 demo5.yslog demo5.smt2 demo5.vcd
|
||||||
rm -f demo6.yslog demo6.smt2
|
rm -f demo6.yslog demo6.smt2
|
||||||
rm -f demo7.yslog demo7.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);
|
module demo2(input clk, input [4:0] addr, output reg [31:0] data);
|
||||||
reg [31:0] mem [0:31];
|
reg [31:0] mem [0:31];
|
||||||
always @(posedge clk)
|
always @(negedge clk)
|
||||||
data <= mem[addr];
|
data <= mem[addr];
|
||||||
|
|
||||||
reg [31:0] used_addr = 0;
|
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
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
* 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
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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;
|
||||||
using namespace AST_INTERNAL;
|
using namespace AST_INTERNAL;
|
||||||
|
|
||||||
// instanciate global variables (public API)
|
// instantiate global variables (public API)
|
||||||
namespace AST {
|
namespace AST {
|
||||||
std::string current_filename;
|
std::string current_filename;
|
||||||
void (*set_line_num)(int) = NULL;
|
void (*set_line_num)(int) = NULL;
|
||||||
int (*get_line_num)() = NULL;
|
int (*get_line_num)() = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// instanciate global variables (private API)
|
// instantiate global variables (private API)
|
||||||
namespace AST_INTERNAL {
|
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;
|
bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire;
|
||||||
AstNode *current_ast, *current_ast_mod;
|
AstNode *current_ast, *current_ast_mod;
|
||||||
std::map<std::string, AstNode*> current_scope;
|
std::map<std::string, AstNode*> current_scope;
|
||||||
|
@ -84,6 +85,9 @@ std::string AST::type2str(AstNodeType type)
|
||||||
X(AST_PREFIX)
|
X(AST_PREFIX)
|
||||||
X(AST_ASSERT)
|
X(AST_ASSERT)
|
||||||
X(AST_ASSUME)
|
X(AST_ASSUME)
|
||||||
|
X(AST_LIVE)
|
||||||
|
X(AST_FAIR)
|
||||||
|
X(AST_COVER)
|
||||||
X(AST_FCALL)
|
X(AST_FCALL)
|
||||||
X(AST_TO_BITS)
|
X(AST_TO_BITS)
|
||||||
X(AST_TO_SIGNED)
|
X(AST_TO_SIGNED)
|
||||||
|
@ -168,8 +172,7 @@ bool AstNode::get_bool_attribute(RTLIL::IdString id)
|
||||||
|
|
||||||
AstNode *attr = attributes.at(id);
|
AstNode *attr = attributes.at(id);
|
||||||
if (attr->type != AST_CONSTANT)
|
if (attr->type != AST_CONSTANT)
|
||||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
log_file_error(attr->filename, attr->linenum, "Attribute `%s' with non-constant value!\n", id.c_str());
|
||||||
id.c_str(), attr->filename.c_str(), attr->linenum);
|
|
||||||
|
|
||||||
return attr->integer != 0;
|
return attr->integer != 0;
|
||||||
}
|
}
|
||||||
|
@ -188,8 +191,10 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
|
||||||
is_input = false;
|
is_input = false;
|
||||||
is_output = false;
|
is_output = false;
|
||||||
is_reg = false;
|
is_reg = false;
|
||||||
|
is_logic = false;
|
||||||
is_signed = false;
|
is_signed = false;
|
||||||
is_string = false;
|
is_string = false;
|
||||||
|
was_checked = false;
|
||||||
range_valid = false;
|
range_valid = false;
|
||||||
range_swapped = false;
|
range_swapped = false;
|
||||||
port_id = 0;
|
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
|
// create a (deep recursive) copy of a node
|
||||||
AstNode *AstNode::clone()
|
AstNode *AstNode::clone() const
|
||||||
{
|
{
|
||||||
AstNode *that = new AstNode;
|
AstNode *that = new AstNode;
|
||||||
*that = *this;
|
*that = *this;
|
||||||
|
@ -221,7 +226,7 @@ AstNode *AstNode::clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a (deep recursive) copy of a node use 'other' as target root node
|
// 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();
|
AstNode *tmp = clone();
|
||||||
other->delete_children();
|
other->delete_children();
|
||||||
|
@ -251,7 +256,7 @@ AstNode::~AstNode()
|
||||||
|
|
||||||
// create a nice text representation of the node
|
// create a nice text representation of the node
|
||||||
// (traverse tree by recursion, use 'other' pointer for diffing two AST trees)
|
// (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) {
|
if (f == NULL) {
|
||||||
for (auto f : log_files)
|
for (auto f : log_files)
|
||||||
|
@ -262,10 +267,12 @@ void AstNode::dumpAst(FILE *f, std::string indent)
|
||||||
std::string type_name = type2str(type);
|
std::string type_name = type2str(type);
|
||||||
fprintf(f, "%s%s <%s:%d>", indent.c_str(), type_name.c_str(), filename.c_str(), linenum);
|
fprintf(f, "%s%s <%s:%d>", indent.c_str(), type_name.c_str(), filename.c_str(), linenum);
|
||||||
|
|
||||||
|
if (!flag_no_dump_ptr) {
|
||||||
if (id2ast)
|
if (id2ast)
|
||||||
fprintf(f, " [%p -> %p]", this, id2ast);
|
fprintf(f, " [%p -> %p]", this, id2ast);
|
||||||
else
|
else
|
||||||
fprintf(f, " [%p]", this);
|
fprintf(f, " [%p]", this);
|
||||||
|
}
|
||||||
|
|
||||||
if (!str.empty())
|
if (!str.empty())
|
||||||
fprintf(f, " str='%s'", str.c_str());
|
fprintf(f, " str='%s'", str.c_str());
|
||||||
|
@ -282,7 +289,9 @@ void AstNode::dumpAst(FILE *f, std::string indent)
|
||||||
fprintf(f, " input");
|
fprintf(f, " input");
|
||||||
if (is_output)
|
if (is_output)
|
||||||
fprintf(f, " 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");
|
fprintf(f, " reg");
|
||||||
if (is_signed)
|
if (is_signed)
|
||||||
fprintf(f, " signed");
|
fprintf(f, " signed");
|
||||||
|
@ -330,7 +339,7 @@ static std::string id2vl(std::string txt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// dump AST node as Verilog pseudo-code
|
// 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;
|
bool first = true;
|
||||||
std::string txt;
|
std::string txt;
|
||||||
|
@ -649,6 +658,8 @@ bool AstNode::operator==(const AstNode &other) const
|
||||||
return false;
|
return false;
|
||||||
if (is_output != other.is_output)
|
if (is_output != other.is_output)
|
||||||
return false;
|
return false;
|
||||||
|
if (is_logic != other.is_logic)
|
||||||
|
return false;
|
||||||
if (is_reg != other.is_reg)
|
if (is_reg != other.is_reg)
|
||||||
return false;
|
return false;
|
||||||
if (is_signed != other.is_signed)
|
if (is_signed != other.is_signed)
|
||||||
|
@ -752,7 +763,7 @@ AstNode *AstNode::mkconst_str(const std::string &str)
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AstNode::bits_only_01()
|
bool AstNode::bits_only_01() const
|
||||||
{
|
{
|
||||||
for (auto bit : bits)
|
for (auto bit : bits)
|
||||||
if (bit != RTLIL::S0 && bit != RTLIL::S1)
|
if (bit != RTLIL::S0 && bit != RTLIL::S1)
|
||||||
|
@ -803,7 +814,7 @@ RTLIL::Const AstNode::asParaConst()
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AstNode::asBool()
|
bool AstNode::asBool() const
|
||||||
{
|
{
|
||||||
log_assert(type == AST_CONSTANT);
|
log_assert(type == AST_CONSTANT);
|
||||||
for (auto &bit : bits)
|
for (auto &bit : bits)
|
||||||
|
@ -812,7 +823,7 @@ bool AstNode::asBool()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int AstNode::isConst()
|
int AstNode::isConst() const
|
||||||
{
|
{
|
||||||
if (type == AST_CONSTANT)
|
if (type == AST_CONSTANT)
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -892,9 +903,9 @@ RTLIL::Const AstNode::realAsConst(int width)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a new AstModule from an AST_MODULE AST node
|
// 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)
|
if (defer)
|
||||||
log("Storing AST representation for module `%s'.\n", ast->str.c_str());
|
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->ast = NULL;
|
||||||
current_module->name = ast->str;
|
current_module->name = ast->str;
|
||||||
current_module->attributes["\\src"] = stringf("%s:%d", ast->filename.c_str(), ast->linenum);
|
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;
|
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) {
|
if (flag_dump_ast1) {
|
||||||
log("Dumping Verilog AST before simplification:\n");
|
log("Dumping Verilog AST before simplification:\n");
|
||||||
|
@ -952,8 +968,7 @@ static AstModule* process_module(AstNode *ast, bool defer)
|
||||||
|
|
||||||
for (auto &attr : ast->attributes) {
|
for (auto &attr : ast->attributes) {
|
||||||
if (attr.second->type != AST_CONSTANT)
|
if (attr.second->type != AST_CONSTANT)
|
||||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||||
attr.first.c_str(), ast->filename.c_str(), ast->linenum);
|
|
||||||
current_module->attributes[attr.first] = attr.second->asAttrConst();
|
current_module->attributes[attr.first] = attr.second->asAttrConst();
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < ast->children.size(); i++) {
|
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();
|
ignoreThisSignalsInInitial = RTLIL::SigSpec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ast->type == AST_INTERFACE)
|
||||||
|
current_module->set_bool_attribute("\\is_interface");
|
||||||
current_module->ast = ast_before_simplify;
|
current_module->ast = ast_before_simplify;
|
||||||
current_module->nolatches = flag_nolatches;
|
current_module->nolatches = flag_nolatches;
|
||||||
current_module->nomeminit = flag_nomeminit;
|
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'
|
// 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,
|
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 ignore_redef, bool defer, bool autowire)
|
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;
|
current_ast = ast;
|
||||||
flag_dump_ast1 = dump_ast1;
|
flag_dump_ast1 = dump_ast1;
|
||||||
flag_dump_ast2 = dump_ast2;
|
flag_dump_ast2 = dump_ast2;
|
||||||
|
flag_no_dump_ptr = no_dump_ptr;
|
||||||
flag_dump_vlog = dump_vlog;
|
flag_dump_vlog = dump_vlog;
|
||||||
flag_dump_rtlil = dump_rtlil;
|
flag_dump_rtlil = dump_rtlil;
|
||||||
flag_nolatches = nolatches;
|
flag_nolatches = nolatches;
|
||||||
|
@ -1016,14 +1034,12 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
|
||||||
flag_icells = icells;
|
flag_icells = icells;
|
||||||
flag_autowire = autowire;
|
flag_autowire = autowire;
|
||||||
|
|
||||||
std::vector<AstNode*> global_decls;
|
|
||||||
|
|
||||||
log_assert(current_ast->type == AST_DESIGN);
|
log_assert(current_ast->type == AST_DESIGN);
|
||||||
for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++)
|
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());
|
(*it)->children.push_back(n->clone());
|
||||||
|
|
||||||
for (auto n : design->verilog_packages){
|
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;
|
(*it)->str = "$abstract" + (*it)->str;
|
||||||
|
|
||||||
if (design->has((*it)->str)) {
|
if (design->has((*it)->str)) {
|
||||||
if (!ignore_redef)
|
RTLIL::Module *existing_mod = design->module((*it)->str);
|
||||||
log_error("Re-definition of module `%s' at %s:%d!\n",
|
if (!nooverwrite && !overwrite && !existing_mod->get_bool_attribute("\\blackbox")) {
|
||||||
(*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum);
|
log_file_error((*it)->filename, (*it)->linenum, "Re-definition of module `%s'!\n", (*it)->str.c_str());
|
||||||
log("Ignoring re-definition of module `%s' at %s:%d!\n",
|
} else if (nooverwrite) {
|
||||||
|
log("Ignoring re-definition of module `%s' at %s:%d.\n",
|
||||||
(*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum);
|
(*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum);
|
||||||
continue;
|
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));
|
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)
|
else if ((*it)->type == AST_PACKAGE)
|
||||||
design->verilog_packages.push_back((*it)->clone());
|
design->verilog_packages.push_back((*it)->clone());
|
||||||
else
|
else
|
||||||
global_decls.push_back(*it);
|
design->verilog_globals.push_back((*it)->clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1065,8 +1088,264 @@ AstModule::~AstModule()
|
||||||
delete ast;
|
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
|
// 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();
|
std::string stripped_name = name.str();
|
||||||
|
|
||||||
|
@ -1105,6 +1384,9 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
|
||||||
rewrite_parameter:
|
rewrite_parameter:
|
||||||
para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
|
para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
|
||||||
delete child->children.at(0);
|
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);
|
child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0);
|
||||||
parameters.erase(para_id);
|
parameters.erase(para_id);
|
||||||
continue;
|
continue;
|
||||||
|
@ -1115,8 +1397,16 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
|
||||||
goto rewrite_parameter;
|
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;
|
std::string modname;
|
||||||
|
|
||||||
|
@ -1127,15 +1417,8 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
|
||||||
else
|
else
|
||||||
modname = "$paramod" + stripped_name + para_info;
|
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;
|
return modname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1177,4 +1460,3 @@ void AST::use_internal_line_num()
|
||||||
}
|
}
|
||||||
|
|
||||||
YOSYS_NAMESPACE_END
|
YOSYS_NAMESPACE_END
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/*
|
/* -*- c++ -*-
|
||||||
* yosys -- Yosys Open SYnthesis Suite
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
|
||||||
|
@ -65,6 +65,9 @@ namespace AST
|
||||||
AST_PREFIX,
|
AST_PREFIX,
|
||||||
AST_ASSERT,
|
AST_ASSERT,
|
||||||
AST_ASSUME,
|
AST_ASSUME,
|
||||||
|
AST_LIVE,
|
||||||
|
AST_FAIR,
|
||||||
|
AST_COVER,
|
||||||
|
|
||||||
AST_FCALL,
|
AST_FCALL,
|
||||||
AST_TO_BITS,
|
AST_TO_BITS,
|
||||||
|
@ -139,6 +142,11 @@ namespace AST
|
||||||
AST_NEGEDGE,
|
AST_NEGEDGE,
|
||||||
AST_EDGE,
|
AST_EDGE,
|
||||||
|
|
||||||
|
AST_INTERFACE,
|
||||||
|
AST_INTERFACEPORT,
|
||||||
|
AST_INTERFACEPORTTYPE,
|
||||||
|
AST_MODPORT,
|
||||||
|
AST_MODPORTMEMBER,
|
||||||
AST_PACKAGE
|
AST_PACKAGE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -165,7 +173,7 @@ namespace AST
|
||||||
// node content - most of it is unused in most node types
|
// node content - most of it is unused in most node types
|
||||||
std::string str;
|
std::string str;
|
||||||
std::vector<RTLIL::State> bits;
|
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;
|
int port_id, range_left, range_right;
|
||||||
uint32_t integer;
|
uint32_t integer;
|
||||||
double realvalue;
|
double realvalue;
|
||||||
|
@ -187,8 +195,8 @@ namespace AST
|
||||||
|
|
||||||
// creating and deleting nodes
|
// creating and deleting nodes
|
||||||
AstNode(AstNodeType type = AST_NONE, AstNode *child1 = NULL, AstNode *child2 = NULL, AstNode *child3 = NULL);
|
AstNode(AstNodeType type = AST_NONE, AstNode *child1 = NULL, AstNode *child2 = NULL, AstNode *child3 = NULL);
|
||||||
AstNode *clone();
|
AstNode *clone() const;
|
||||||
void cloneInto(AstNode *other);
|
void cloneInto(AstNode *other) const;
|
||||||
void delete_children();
|
void delete_children();
|
||||||
~AstNode();
|
~AstNode();
|
||||||
|
|
||||||
|
@ -231,8 +239,8 @@ namespace AST
|
||||||
AstNode *eval_const_function(AstNode *fcall);
|
AstNode *eval_const_function(AstNode *fcall);
|
||||||
|
|
||||||
// create a human-readable text representation of the AST (for debugging)
|
// create a human-readable text representation of the AST (for debugging)
|
||||||
void dumpAst(FILE *f, std::string indent);
|
void dumpAst(FILE *f, std::string indent) const;
|
||||||
void dumpVlog(FILE *f, std::string indent);
|
void dumpVlog(FILE *f, std::string indent) const;
|
||||||
|
|
||||||
// used by genRTLIL() for detecting expression width and sign
|
// used by genRTLIL() for detecting expression width and sign
|
||||||
void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL);
|
void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL);
|
||||||
|
@ -261,27 +269,30 @@ namespace AST
|
||||||
RTLIL::Const asAttrConst();
|
RTLIL::Const asAttrConst();
|
||||||
RTLIL::Const asParaConst();
|
RTLIL::Const asParaConst();
|
||||||
uint64_t asInt(bool is_signed);
|
uint64_t asInt(bool is_signed);
|
||||||
bool bits_only_01();
|
bool bits_only_01() const;
|
||||||
bool asBool();
|
bool asBool() const;
|
||||||
|
|
||||||
// helper functions for real valued const eval
|
// 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);
|
double asReal(bool is_signed);
|
||||||
RTLIL::Const realAsConst(int width);
|
RTLIL::Const realAsConst(int width);
|
||||||
};
|
};
|
||||||
|
|
||||||
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
|
// 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,
|
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 ignore_redef, bool defer, bool autowire);
|
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
|
// parametric modules are supported directly by the AST library
|
||||||
// therefore we need our own derivate of RTLIL::Module with overloaded virtual functions
|
// therefore we need our own derivate of RTLIL::Module with overloaded virtual functions
|
||||||
struct AstModule : RTLIL::Module {
|
struct AstModule : RTLIL::Module {
|
||||||
AstNode *ast;
|
AstNode *ast;
|
||||||
bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire;
|
bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire;
|
||||||
virtual ~AstModule();
|
~AstModule() YS_OVERRIDE;
|
||||||
virtual RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters);
|
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE;
|
||||||
virtual RTLIL::Module *clone() const;
|
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
|
// this must be set by the language frontend before parsing the sources
|
||||||
|
@ -297,12 +308,17 @@ namespace AST
|
||||||
|
|
||||||
// call a DPI function
|
// 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);
|
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
|
namespace AST_INTERNAL
|
||||||
{
|
{
|
||||||
// internal state variables
|
// 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 bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire;
|
||||||
extern AST::AstNode *current_ast, *current_ast_mod;
|
extern AST::AstNode *current_ast, *current_ast_mod;
|
||||||
extern std::map<std::string, AST::AstNode*> current_scope;
|
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)
|
if (gen_attributes)
|
||||||
for (auto &attr : that->attributes) {
|
for (auto &attr : that->attributes) {
|
||||||
if (attr.second->type != AST_CONSTANT)
|
if (attr.second->type != AST_CONSTANT)
|
||||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||||
attr.first.c_str(), that->filename.c_str(), that->linenum);
|
|
||||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
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)
|
if (that != NULL)
|
||||||
for (auto &attr : that->attributes) {
|
for (auto &attr : that->attributes) {
|
||||||
if (attr.second->type != AST_CONSTANT)
|
if (attr.second->type != AST_CONSTANT)
|
||||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||||
attr.first.c_str(), that->filename.c_str(), that->linenum);
|
|
||||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
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) {
|
for (auto &attr : that->attributes) {
|
||||||
if (attr.second->type != AST_CONSTANT)
|
if (attr.second->type != AST_CONSTANT)
|
||||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||||
attr.first.c_str(), that->filename.c_str(), that->linenum);
|
|
||||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
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) {
|
for (auto &attr : that->attributes) {
|
||||||
if (attr.second->type != AST_CONSTANT)
|
if (attr.second->type != AST_CONSTANT)
|
||||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||||
attr.first.c_str(), that->filename.c_str(), that->linenum);
|
|
||||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
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++);
|
proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->linenum, autoidx++);
|
||||||
for (auto &attr : always->attributes) {
|
for (auto &attr : always->attributes) {
|
||||||
if (attr.second->type != AST_CONSTANT)
|
if (attr.second->type != AST_CONSTANT)
|
||||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
log_file_error(always->filename, always->linenum, "Attribute `%s' with non-constant value!\n",
|
||||||
attr.first.c_str(), always->filename.c_str(), always->linenum);
|
attr.first.c_str());
|
||||||
proc->attributes[attr.first] = attr.second->asAttrConst();
|
proc->attributes[attr.first] = attr.second->asAttrConst();
|
||||||
}
|
}
|
||||||
current_module->processes[proc->name] = proc;
|
current_module->processes[proc->name] = proc;
|
||||||
|
@ -223,16 +219,22 @@ struct AST_INTERNAL::ProcessGenerator
|
||||||
bool found_global_syncs = false;
|
bool found_global_syncs = false;
|
||||||
bool found_anyedge_syncs = false;
|
bool found_anyedge_syncs = false;
|
||||||
for (auto child : always->children)
|
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 (child->type == AST_EDGE) {
|
||||||
if (GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->str == "\\$global_clock")
|
if (GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->str == "\\$global_clock")
|
||||||
found_global_syncs = true;
|
found_global_syncs = true;
|
||||||
else
|
else
|
||||||
found_anyedge_syncs = true;
|
found_anyedge_syncs = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (found_anyedge_syncs) {
|
if (found_anyedge_syncs) {
|
||||||
if (found_global_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("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("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");
|
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;
|
bool found_clocked_sync = false;
|
||||||
for (auto child : always->children)
|
for (auto child : always->children)
|
||||||
if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) {
|
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;
|
found_clocked_sync = true;
|
||||||
if (found_global_syncs || found_anyedge_syncs)
|
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;
|
RTLIL::SyncRule *syncrule = new RTLIL::SyncRule;
|
||||||
syncrule->type = child->type == AST_POSEDGE ? RTLIL::STp : RTLIL::STn;
|
syncrule->type = child->type == AST_POSEDGE ? RTLIL::STp : RTLIL::STn;
|
||||||
syncrule->signal = child->children[0]->genRTLIL();
|
syncrule->signal = child->children[0]->genRTLIL();
|
||||||
if (GetSize(syncrule->signal) != 1)
|
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);
|
addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, true);
|
||||||
proc->syncs.push_back(syncrule);
|
proc->syncs.push_back(syncrule);
|
||||||
}
|
}
|
||||||
|
@ -471,8 +476,7 @@ struct AST_INTERNAL::ProcessGenerator
|
||||||
|
|
||||||
for (auto &attr : ast->attributes) {
|
for (auto &attr : ast->attributes) {
|
||||||
if (attr.second->type != AST_CONSTANT)
|
if (attr.second->type != AST_CONSTANT)
|
||||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||||
attr.first.c_str(), ast->filename.c_str(), ast->linenum);
|
|
||||||
sw->attributes[attr.first] = attr.second->asAttrConst();
|
sw->attributes[attr.first] = attr.second->asAttrConst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,12 +544,12 @@ struct AST_INTERNAL::ProcessGenerator
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AST_WIRE:
|
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;
|
break;
|
||||||
|
|
||||||
case AST_PARAMETER:
|
case AST_PARAMETER:
|
||||||
case AST_LOCALPARAM:
|
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;
|
break;
|
||||||
|
|
||||||
case AST_NONE:
|
case AST_NONE:
|
||||||
|
@ -554,6 +558,8 @@ struct AST_INTERNAL::ProcessGenerator
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
// ast->dumpAst(NULL, "ast> ");
|
||||||
|
// current_ast_mod->dumpAst(NULL, "mod> ");
|
||||||
log_abort();
|
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))
|
if (id_ast == NULL && current_scope.count(str))
|
||||||
id_ast = current_scope.at(str);
|
id_ast = current_scope.at(str);
|
||||||
if (!id_ast)
|
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->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM) {
|
||||||
if (id_ast->children.size() > 1 && id_ast->children[1]->range_valid) {
|
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;
|
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)
|
if (id_ast->children[0]->type == AST_CONSTANT)
|
||||||
this_width = id_ast->children[0]->bits.size();
|
this_width = id_ast->children[0]->bits.size();
|
||||||
else
|
else
|
||||||
log_error("Failed to detect width for parameter %s at %s:%d!\n", str.c_str(), filename.c_str(), linenum);
|
log_file_error(filename, linenum, "Failed to detect width for parameter %s!\n", str.c_str());
|
||||||
if (children.size() != 0)
|
if (children.size() != 0)
|
||||||
range = children[0];
|
range = children[0];
|
||||||
} else if (id_ast->type == AST_WIRE || id_ast->type == AST_AUTOWIRE) {
|
} 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");
|
// log("---\n");
|
||||||
// id_ast->dumpAst(NULL, "decl> ");
|
// id_ast->dumpAst(NULL, "decl> ");
|
||||||
// dumpAst(NULL, "ref> ");
|
// 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 {
|
} else {
|
||||||
this_width = id_ast->range_left - id_ast->range_right + 1;
|
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;
|
this_width = 32;
|
||||||
} else if (id_ast->type == AST_MEMORY) {
|
} else if (id_ast->type == AST_MEMORY) {
|
||||||
if (!id_ast->children[0]->range_valid)
|
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;
|
this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1;
|
||||||
} else
|
} 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) {
|
||||||
if (range->children.size() == 1)
|
if (range->children.size() == 1)
|
||||||
this_width = 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 (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)) { }
|
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)
|
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",
|
log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
|
||||||
str.c_str(), filename.c_str(), linenum);
|
|
||||||
this_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
|
this_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
|
||||||
delete left_at_zero_ast;
|
delete left_at_zero_ast;
|
||||||
delete right_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:
|
case AST_TO_BITS:
|
||||||
while (children[0]->simplify(true, false, false, 1, -1, false, false) == true) { }
|
while (children[0]->simplify(true, false, false, 1, -1, false, false) == true) { }
|
||||||
if (children[0]->type != AST_CONSTANT)
|
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);
|
children[1]->detectSignWidthWorker(sub_width_hint, sign_hint);
|
||||||
width_hint = max(width_hint, children[0]->bitsAsConst().as_int());
|
width_hint = max(width_hint, children[0]->bitsAsConst().as_int());
|
||||||
break;
|
break;
|
||||||
|
@ -682,7 +687,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
||||||
case AST_REPLICATE:
|
case AST_REPLICATE:
|
||||||
while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { }
|
while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { }
|
||||||
if (children[0]->type != AST_CONSTANT)
|
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);
|
children[1]->detectSignWidthWorker(sub_width_hint, sub_sign_hint);
|
||||||
width_hint = max(width_hint, children[0]->bitsAsConst().as_int() * sub_width_hint);
|
width_hint = max(width_hint, children[0]->bitsAsConst().as_int() * sub_width_hint);
|
||||||
sign_hint = false;
|
sign_hint = false;
|
||||||
|
@ -756,18 +761,18 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
||||||
if (!id2ast->is_signed)
|
if (!id2ast->is_signed)
|
||||||
sign_hint = false;
|
sign_hint = false;
|
||||||
if (!id2ast->children[0]->range_valid)
|
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;
|
this_width = id2ast->children[0]->range_left - id2ast->children[0]->range_right + 1;
|
||||||
width_hint = max(width_hint, this_width);
|
width_hint = max(width_hint, this_width);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AST_FCALL:
|
case AST_FCALL:
|
||||||
if (str == "\\$anyconst" || str == "\\$anyseq") {
|
if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") {
|
||||||
if (GetSize(children) == 1) {
|
if (GetSize(children) == 1) {
|
||||||
while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { }
|
while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { }
|
||||||
if (children[0]->type != AST_CONSTANT)
|
if (children[0]->type != AST_CONSTANT)
|
||||||
log_error("System function %s called with non-const argument at %s:%d!\n",
|
log_file_error(filename, linenum, "System function %s called with non-const argument!\n",
|
||||||
RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
|
RTLIL::unescape_id(str).c_str());
|
||||||
width_hint = max(width_hint, int(children[0]->asInt(true)));
|
width_hint = max(width_hint, int(children[0]->asInt(true)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -788,8 +793,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
|
||||||
default:
|
default:
|
||||||
for (auto f : log_files)
|
for (auto f : log_files)
|
||||||
current_ast->dumpAst(f, "verilog-ast> ");
|
current_ast->dumpAst(f, "verilog-ast> ");
|
||||||
log_error("Don't know how to detect sign and width for %s node at %s:%d!\n",
|
log_file_error(filename, linenum, "Don't know how to detect sign and width for %s node!\n", type2str(type).c_str());
|
||||||
type2str(type).c_str(), filename.c_str(), linenum);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*found_real)
|
if (*found_real)
|
||||||
|
@ -842,6 +846,35 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
case AST_GENIF:
|
case AST_GENIF:
|
||||||
case AST_GENCASE:
|
case AST_GENCASE:
|
||||||
case AST_PACKAGE:
|
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;
|
break;
|
||||||
|
|
||||||
// remember the parameter, needed for example in techmap
|
// 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
|
// create an RTLIL::Wire for an AST_WIRE node
|
||||||
case AST_WIRE: {
|
case AST_WIRE: {
|
||||||
if (current_module->wires_.count(str) != 0)
|
if (current_module->wires_.count(str) != 0)
|
||||||
log_error("Re-definition of signal `%s' at %s:%d!\n",
|
log_file_error(filename, linenum, "Re-definition of signal `%s'!\n", str.c_str());
|
||||||
str.c_str(), filename.c_str(), linenum);
|
|
||||||
if (!range_valid)
|
if (!range_valid)
|
||||||
log_error("Signal `%s' with non-constant width at %s:%d!\n",
|
log_file_error(filename, linenum, "Signal `%s' with non-constant width!\n", str.c_str());
|
||||||
str.c_str(), filename.c_str(), linenum);
|
|
||||||
|
|
||||||
log_assert(range_left >= range_right || (range_left == -1 && range_right == 0));
|
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) {
|
for (auto &attr : attributes) {
|
||||||
if (attr.second->type != AST_CONSTANT)
|
if (attr.second->type != AST_CONSTANT)
|
||||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||||
attr.first.c_str(), filename.c_str(), linenum);
|
|
||||||
wire->attributes[attr.first] = attr.second->asAttrConst();
|
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
|
// create an RTLIL::Memory for an AST_MEMORY node
|
||||||
case AST_MEMORY: {
|
case AST_MEMORY: {
|
||||||
if (current_module->memories.count(str) != 0)
|
if (current_module->memories.count(str) != 0)
|
||||||
log_error("Re-definition of memory `%s' at %s:%d!\n",
|
log_file_error(filename, linenum, "Re-definition of memory `%s'!\n", str.c_str());
|
||||||
str.c_str(), filename.c_str(), linenum);
|
|
||||||
|
|
||||||
log_assert(children.size() >= 2);
|
log_assert(children.size() >= 2);
|
||||||
log_assert(children[0]->type == AST_RANGE);
|
log_assert(children[0]->type == AST_RANGE);
|
||||||
log_assert(children[1]->type == AST_RANGE);
|
log_assert(children[1]->type == AST_RANGE);
|
||||||
|
|
||||||
if (!children[0]->range_valid || !children[1]->range_valid)
|
if (!children[0]->range_valid || !children[1]->range_valid)
|
||||||
log_error("Memory `%s' with non-constant width or size at %s:%d!\n",
|
log_file_error(filename, linenum, "Memory `%s' with non-constant width or size!\n", str.c_str());
|
||||||
str.c_str(), filename.c_str(), linenum);
|
|
||||||
|
|
||||||
RTLIL::Memory *memory = new RTLIL::Memory;
|
RTLIL::Memory *memory = new RTLIL::Memory;
|
||||||
memory->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
|
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) {
|
for (auto &attr : attributes) {
|
||||||
if (attr.second->type != AST_CONSTANT)
|
if (attr.second->type != AST_CONSTANT)
|
||||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||||
attr.first.c_str(), filename.c_str(), linenum);
|
|
||||||
memory->attributes[attr.first] = attr.second->asAttrConst();
|
memory->attributes[attr.first] = attr.second->asAttrConst();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -926,8 +953,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
case AST_REALVALUE:
|
case AST_REALVALUE:
|
||||||
{
|
{
|
||||||
RTLIL::SigSpec sig = realAsConst(width_hint);
|
RTLIL::SigSpec sig = realAsConst(width_hint);
|
||||||
log_warning("converting real value %e to binary %s at %s:%d.\n",
|
log_file_warning(filename, linenum, "converting real value %e to binary %s.\n", realvalue, log_signal(sig));
|
||||||
realvalue, log_signal(sig), filename.c_str(), linenum);
|
|
||||||
return sig;
|
return sig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -938,6 +964,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
{
|
{
|
||||||
RTLIL::Wire *wire = NULL;
|
RTLIL::Wire *wire = NULL;
|
||||||
RTLIL::SigChunk chunk;
|
RTLIL::SigChunk chunk;
|
||||||
|
bool is_interface = false;
|
||||||
|
|
||||||
int add_undef_bits_msb = 0;
|
int add_undef_bits_msb = 0;
|
||||||
int add_undef_bits_lsb = 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->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
|
||||||
wire->name = str;
|
wire->name = str;
|
||||||
if (flag_autowire)
|
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
|
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) {
|
else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM) {
|
||||||
if (id2ast->children[0]->type != AST_CONSTANT)
|
if (id2ast->children[0]->type != AST_CONSTANT)
|
||||||
log_error("Parameter %s does not evaluate to constant value at %s:%d!\n",
|
log_file_error(filename, linenum, "Parameter %s does not evaluate to constant value!\n", str.c_str());
|
||||||
str.c_str(), filename.c_str(), linenum);
|
|
||||||
chunk = RTLIL::Const(id2ast->children[0]->bits);
|
chunk = RTLIL::Const(id2ast->children[0]->bits);
|
||||||
goto use_const_chunk;
|
goto use_const_chunk;
|
||||||
}
|
}
|
||||||
else if (!id2ast || (id2ast->type != AST_WIRE && id2ast->type != AST_AUTOWIRE &&
|
else if (id2ast && (id2ast->type == AST_WIRE || id2ast->type == AST_AUTOWIRE || id2ast->type == AST_MEMORY) && current_module->wires_.count(str) != 0) {
|
||||||
id2ast->type != AST_MEMORY) || current_module->wires_.count(str) == 0)
|
RTLIL::Wire *current_wire = current_module->wire(str);
|
||||||
log_error("Identifier `%s' doesn't map to any signal at %s:%d!\n",
|
if (current_wire->get_bool_attribute("\\is_interface"))
|
||||||
str.c_str(), filename.c_str(), linenum);
|
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)
|
if (id2ast->type == AST_MEMORY)
|
||||||
log_error("Identifier `%s' does map to an unexpanded memory at %s:%d!\n",
|
log_file_error(filename, linenum, "Identifier `%s' does map to an unexpanded memory!\n", str.c_str());
|
||||||
str.c_str(), filename.c_str(), linenum);
|
|
||||||
|
// 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];
|
wire = current_module->wires_[str];
|
||||||
chunk.wire = wire;
|
chunk.wire = wire;
|
||||||
|
@ -974,7 +1024,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
|
|
||||||
use_const_chunk:
|
use_const_chunk:
|
||||||
if (children.size() != 0) {
|
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_width = id2ast->range_left - id2ast->range_right + 1;
|
||||||
int source_offset = id2ast->range_right;
|
int source_offset = id2ast->range_right;
|
||||||
if (!children[0]->range_valid) {
|
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 (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)) { }
|
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)
|
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",
|
log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
|
||||||
str.c_str(), filename.c_str(), linenum);
|
|
||||||
int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
|
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 ?
|
AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ?
|
||||||
children[0]->children[1]->clone() : children[0]->children[0]->clone());
|
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);
|
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.offset >= source_width || chunk.offset + chunk.width < 0) {
|
||||||
if (chunk.width == 1)
|
if (chunk.width == 1)
|
||||||
log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting result bit to undef.\n",
|
log_file_warning(filename, linenum, "Range select out of bounds on signal `%s': Setting result bit to undef.\n",
|
||||||
str.c_str(), filename.c_str(), linenum);
|
str.c_str());
|
||||||
else
|
else
|
||||||
log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting all %d result bits to undef.\n",
|
log_file_warning(filename, linenum, "Range select [%d:%d] out of bounds on signal `%s': Setting all %d result bits to undef.\n",
|
||||||
str.c_str(), filename.c_str(), linenum, chunk.width);
|
children[0]->range_left, children[0]->range_right, str.c_str(), chunk.width);
|
||||||
chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width);
|
chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width);
|
||||||
} else {
|
} else {
|
||||||
if (chunk.width + chunk.offset > source_width) {
|
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;
|
chunk.offset += add_undef_bits_lsb;
|
||||||
}
|
}
|
||||||
if (add_undef_bits_lsb)
|
if (add_undef_bits_lsb)
|
||||||
log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting %d LSB bits to undef.\n",
|
log_file_warning(filename, linenum, "Range [%d:%d] select out of bounds on signal `%s': Setting %d LSB bits to undef.\n",
|
||||||
str.c_str(), filename.c_str(), linenum, add_undef_bits_lsb);
|
children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_lsb);
|
||||||
if (add_undef_bits_msb)
|
if (add_undef_bits_msb)
|
||||||
log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting %d MSB bits to undef.\n",
|
log_file_warning(filename, linenum, "Range [%d:%d] select out of bounds on signal `%s': Setting %d MSB bits to undef.\n",
|
||||||
str.c_str(), filename.c_str(), linenum, add_undef_bits_msb);
|
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 left = children[0]->genRTLIL();
|
||||||
RTLIL::SigSpec right = children[1]->genRTLIL();
|
RTLIL::SigSpec right = children[1]->genRTLIL();
|
||||||
if (!left.is_fully_const())
|
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();
|
int count = left.as_int();
|
||||||
RTLIL::SigSpec sig;
|
RTLIL::SigSpec sig;
|
||||||
for (int i = 0; i < count; i++)
|
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["\\CLK_POLARITY"] = RTLIL::Const(0);
|
||||||
cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
|
cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
|
||||||
|
|
||||||
|
if (!sign_hint)
|
||||||
|
is_signed = false;
|
||||||
|
|
||||||
return RTLIL::SigSpec(wire);
|
return RTLIL::SigSpec(wire);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1308,7 +1361,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
int num_words = 1;
|
int num_words = 1;
|
||||||
if (type == AST_MEMINIT) {
|
if (type == AST_MEMINIT) {
|
||||||
if (children[2]->type != AST_CONSTANT)
|
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));
|
num_words = int(children[2]->asInt(false));
|
||||||
cell->parameters["\\WORDS"] = RTLIL::Const(num_words);
|
cell->parameters["\\WORDS"] = RTLIL::Const(num_words);
|
||||||
}
|
}
|
||||||
|
@ -1336,9 +1389,16 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
// generate $assert cells
|
// generate $assert cells
|
||||||
case AST_ASSERT:
|
case AST_ASSERT:
|
||||||
case AST_ASSUME:
|
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_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);
|
log_assert(children.size() == 2);
|
||||||
|
|
||||||
|
@ -1358,8 +1418,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
|
|
||||||
for (auto &attr : attributes) {
|
for (auto &attr : attributes) {
|
||||||
if (attr.second->type != AST_CONSTANT)
|
if (attr.second->type != AST_CONSTANT)
|
||||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||||
attr.first.c_str(), filename.c_str(), linenum);
|
|
||||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
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_left.append(left[i]);
|
||||||
new_right.append(right[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",
|
" 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));
|
log_signal(new_left), log_signal(new_right));
|
||||||
left = new_left;
|
left = new_left;
|
||||||
right = new_right;
|
right = new_right;
|
||||||
|
@ -1397,11 +1456,12 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
int port_counter = 0, para_counter = 0;
|
int port_counter = 0, para_counter = 0;
|
||||||
|
|
||||||
if (current_module->count_id(str) != 0)
|
if (current_module->count_id(str) != 0)
|
||||||
log_error("Re-definition of cell `%s' at %s:%d!\n",
|
log_file_error(filename, linenum, "Re-definition of cell `%s'!\n", str.c_str());
|
||||||
str.c_str(), filename.c_str(), linenum);
|
|
||||||
|
|
||||||
RTLIL::Cell *cell = current_module->addCell(str, "");
|
RTLIL::Cell *cell = current_module->addCell(str, "");
|
||||||
cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
|
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++) {
|
for (auto it = children.begin(); it != children.end(); it++) {
|
||||||
AstNode *child = *it;
|
AstNode *child = *it;
|
||||||
|
@ -1414,16 +1474,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
if (child->type == AST_PARASET) {
|
if (child->type == AST_PARASET) {
|
||||||
IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str;
|
IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str;
|
||||||
if (child->children[0]->type == AST_REALVALUE) {
|
if (child->children[0]->type == AST_REALVALUE) {
|
||||||
log_warning("Replacing floating point parameter %s.%s = %f with string at %s:%d.\n",
|
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,
|
log_id(cell), log_id(paraname), child->children[0]->realvalue);
|
||||||
filename.c_str(), linenum);
|
|
||||||
auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue));
|
auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue));
|
||||||
strnode->cloneInto(child->children[0]);
|
strnode->cloneInto(child->children[0]);
|
||||||
delete strnode;
|
delete strnode;
|
||||||
}
|
}
|
||||||
if (child->children[0]->type != AST_CONSTANT)
|
if (child->children[0]->type != AST_CONSTANT)
|
||||||
log_error("Parameter %s.%s with non-constant value at %s:%d!\n",
|
log_file_error(filename, linenum, "Parameter %s.%s with non-constant value!\n",
|
||||||
log_id(cell), log_id(paraname), filename.c_str(), linenum);
|
log_id(cell), log_id(paraname));
|
||||||
cell->parameters[paraname] = child->children[0]->asParaConst();
|
cell->parameters[paraname] = child->children[0]->asParaConst();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1444,8 +1503,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
}
|
}
|
||||||
for (auto &attr : attributes) {
|
for (auto &attr : attributes) {
|
||||||
if (attr.second->type != AST_CONSTANT)
|
if (attr.second->type != AST_CONSTANT)
|
||||||
log_error("Attribute `%s' with non-constant value at %s:%d!\n",
|
log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
|
||||||
attr.first.c_str(), filename.c_str(), linenum);
|
|
||||||
cell->attributes[attr.first] = attr.second->asAttrConst();
|
cell->attributes[attr.first] = attr.second->asAttrConst();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1466,30 +1524,36 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case AST_FCALL: {
|
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++);
|
string myid = stringf("%s$%d", str.c_str() + 1, autoidx++);
|
||||||
int width = width_hint;
|
int width = width_hint;
|
||||||
|
|
||||||
if (GetSize(children) > 1)
|
if (GetSize(children) > 1)
|
||||||
log_error("System function %s got %d arguments, expected 1 or 0 at %s:%d.\n",
|
log_file_error(filename, linenum, "System function %s got %d arguments, expected 1 or 0.\n",
|
||||||
RTLIL::unescape_id(str).c_str(), GetSize(children), filename.c_str(), linenum);
|
RTLIL::unescape_id(str).c_str(), GetSize(children));
|
||||||
|
|
||||||
if (GetSize(children) == 1) {
|
if (GetSize(children) == 1) {
|
||||||
if (children[0]->type != AST_CONSTANT)
|
if (children[0]->type != AST_CONSTANT)
|
||||||
log_error("System function %s called with non-const argument at %s:%d!\n",
|
log_file_error(filename, linenum, "System function %s called with non-const argument!\n",
|
||||||
RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
|
RTLIL::unescape_id(str).c_str());
|
||||||
width = children[0]->asInt(true);
|
width = children[0]->asInt(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (width <= 0)
|
if (width <= 0)
|
||||||
log_error("Failed to detect width of %s at %s:%d!\n",
|
log_file_error(filename, linenum, "Failed to detect width of %s!\n", RTLIL::unescape_id(str).c_str());
|
||||||
RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum);
|
|
||||||
|
|
||||||
Cell *cell = current_module->addCell(myid, str.substr(1));
|
Cell *cell = current_module->addCell(myid, str.substr(1));
|
||||||
cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
|
cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
|
||||||
cell->parameters["\\WIDTH"] = width;
|
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 *wire = current_module->addWire(myid + "_wire", width);
|
||||||
wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
|
wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
|
||||||
cell->setPort("\\Y", wire);
|
cell->setPort("\\Y", wire);
|
||||||
|
@ -1504,8 +1568,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
||||||
for (auto f : log_files)
|
for (auto f : log_files)
|
||||||
current_ast->dumpAst(f, "verilog-ast> ");
|
current_ast->dumpAst(f, "verilog-ast> ");
|
||||||
type_name = type2str(type);
|
type_name = type2str(type);
|
||||||
log_error("Don't know how to generate RTLIL code for %s node at %s:%d!\n",
|
log_file_error(filename, linenum, "Don't know how to generate RTLIL code for %s node!\n", type_name.c_str());
|
||||||
type_name.c_str(), filename.c_str(), linenum);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return RTLIL::SigSpec();
|
return RTLIL::SigSpec();
|
||||||
|
@ -1535,4 +1598,3 @@ RTLIL::SigSpec AstNode::genWidthRTLIL(int width, const dict<RTLIL::SigBit, RTLIL
|
||||||
}
|
}
|
||||||
|
|
||||||
YOSYS_NAMESPACE_END
|
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::Module *module = nullptr;
|
||||||
RTLIL::Const *lutptr = NULL;
|
RTLIL::Const *lutptr = NULL;
|
||||||
RTLIL::Cell *sopcell = NULL;
|
RTLIL::Cell *sopcell = NULL;
|
||||||
|
RTLIL::Cell *lastcell = nullptr;
|
||||||
RTLIL::State lut_default_state = RTLIL::State::Sx;
|
RTLIL::State lut_default_state = RTLIL::State::Sx;
|
||||||
|
std::string err_reason;
|
||||||
int blif_maxnum = 0, sopmode = -1;
|
int blif_maxnum = 0, sopmode = -1;
|
||||||
|
|
||||||
auto blif_wire = [&](const std::string &wire_name) -> Wire*
|
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_attributes = nullptr;
|
||||||
dict<RTLIL::IdString, RTLIL::Const> *obj_parameters = nullptr;
|
dict<RTLIL::IdString, RTLIL::Const> *obj_parameters = nullptr;
|
||||||
|
|
||||||
|
dict<RTLIL::IdString, std::pair<int, bool>> wideports_cache;
|
||||||
|
|
||||||
size_t buffer_size = 4096;
|
size_t buffer_size = 4096;
|
||||||
char *buffer = (char*)malloc(buffer_size);
|
char *buffer = (char*)malloc(buffer_size);
|
||||||
int line_count = 0;
|
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)
|
if (module != nullptr)
|
||||||
goto error;
|
goto error;
|
||||||
module = new RTLIL::Module;
|
module = new RTLIL::Module;
|
||||||
|
lastcell = nullptr;
|
||||||
module->name = RTLIL::escape_id(strtok(NULL, " \t\r\n"));
|
module->name = RTLIL::escape_id(strtok(NULL, " \t\r\n"));
|
||||||
obj_attributes = &module->attributes;
|
obj_attributes = &module->attributes;
|
||||||
obj_parameters = nullptr;
|
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"))
|
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();
|
module->fixup_ports();
|
||||||
|
wideports_cache.clear();
|
||||||
|
|
||||||
if (run_clean)
|
if (run_clean)
|
||||||
{
|
{
|
||||||
|
@ -182,14 +235,17 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
||||||
}
|
}
|
||||||
|
|
||||||
module = nullptr;
|
module = nullptr;
|
||||||
|
lastcell = nullptr;
|
||||||
obj_attributes = nullptr;
|
obj_attributes = nullptr;
|
||||||
obj_parameters = nullptr;
|
obj_parameters = nullptr;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs")) {
|
if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs"))
|
||||||
|
{
|
||||||
char *p;
|
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::IdString wire_name(stringf("\\%s", p));
|
||||||
RTLIL::Wire *wire = module->wire(wire_name);
|
RTLIL::Wire *wire = module->wire(wire_name);
|
||||||
if (wire == nullptr)
|
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;
|
wire->port_input = true;
|
||||||
else
|
else
|
||||||
wire->port_output = true;
|
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_attributes = nullptr;
|
||||||
obj_parameters = nullptr;
|
obj_parameters = nullptr;
|
||||||
continue;
|
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")) {
|
if (!strcmp(cmd, ".attr") || !strcmp(cmd, ".param")) {
|
||||||
char *n = strtok(NULL, " \t\r\n");
|
char *n = strtok(NULL, " \t\r\n");
|
||||||
char *v = strtok(NULL, "\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;
|
const_v.bits[i] = v[n-i-1] != '0' ? State::S1 : State::S0;
|
||||||
}
|
}
|
||||||
if (!strcmp(cmd, ".attr")) {
|
if (!strcmp(cmd, ".attr")) {
|
||||||
if (obj_attributes == nullptr)
|
if (obj_attributes == nullptr) {
|
||||||
goto error;
|
err_reason = stringf("No object to attach .attr too.");
|
||||||
|
goto error_with_reason;
|
||||||
|
}
|
||||||
(*obj_attributes)[id_n] = const_v;
|
(*obj_attributes)[id_n] = const_v;
|
||||||
} else {
|
} else {
|
||||||
if (obj_parameters == nullptr)
|
if (obj_parameters == nullptr) {
|
||||||
goto error;
|
err_reason = stringf("No object to attach .param too.");
|
||||||
|
goto error_with_reason;
|
||||||
|
}
|
||||||
(*obj_parameters)[id_n] = const_v;
|
(*obj_parameters)[id_n] = const_v;
|
||||||
}
|
}
|
||||||
continue;
|
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_attributes = &cell->attributes;
|
||||||
obj_parameters = &cell->parameters;
|
obj_parameters = &cell->parameters;
|
||||||
continue;
|
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);
|
IdString celltype = RTLIL::escape_id(p);
|
||||||
RTLIL::Cell *cell = module->addCell(NEW_ID, celltype);
|
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, '=');
|
char *q = strchr(p, '=');
|
||||||
if (q == NULL || !q[0])
|
if (q == NULL || !q[0])
|
||||||
goto error;
|
goto error;
|
||||||
*(q++) = 0;
|
*(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());
|
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_attributes = &cell->attributes;
|
||||||
obj_parameters = &cell->parameters;
|
obj_parameters = &cell->parameters;
|
||||||
continue;
|
continue;
|
||||||
|
@ -301,7 +417,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
||||||
obj_attributes = nullptr;
|
obj_attributes = nullptr;
|
||||||
obj_parameters = nullptr;
|
obj_parameters = nullptr;
|
||||||
|
|
||||||
if (!strcmp(cmd, ".barbuf"))
|
if (!strcmp(cmd, ".barbuf") || !strcmp(cmd, ".conn"))
|
||||||
{
|
{
|
||||||
char *p = strtok(NULL, " \t\r\n");
|
char *p = strtok(NULL, " \t\r\n");
|
||||||
if (p == NULL)
|
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("\\A", input_sig);
|
||||||
sopcell->setPort("\\Y", output_sig);
|
sopcell->setPort("\\Y", output_sig);
|
||||||
sopmode = -1;
|
sopmode = -1;
|
||||||
|
lastcell = sopcell;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -379,6 +496,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
||||||
cell->setPort("\\Y", output_sig);
|
cell->setPort("\\Y", output_sig);
|
||||||
lutptr = &cell->parameters.at("\\LUT");
|
lutptr = &cell->parameters.at("\\LUT");
|
||||||
lut_default_state = RTLIL::State::Sx;
|
lut_default_state = RTLIL::State::Sx;
|
||||||
|
lastcell = cell;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -432,7 +550,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
|
||||||
|
|
||||||
if (lutptr)
|
if (lutptr)
|
||||||
{
|
{
|
||||||
if (input_len > 8)
|
if (input_len > 12)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
for (int i = 0; i < (1 << input_len); i++) {
|
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:
|
error:
|
||||||
log_error("Syntax error in line %d!\n", line_count);
|
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 {
|
struct BlifFrontend : public Frontend {
|
||||||
BlifFrontend() : Frontend("blif", "read BLIF file") { }
|
BlifFrontend() : Frontend("blif", "read BLIF file") { }
|
||||||
virtual void help()
|
void help() YS_OVERRIDE
|
||||||
{
|
{
|
||||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
log("\n");
|
||||||
|
@ -469,10 +591,15 @@ struct BlifFrontend : public Frontend {
|
||||||
log(" -sop\n");
|
log(" -sop\n");
|
||||||
log(" Create $sop cells instead of $lut cells\n");
|
log(" Create $sop cells instead of $lut cells\n");
|
||||||
log("\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 sop_mode = false;
|
||||||
|
bool wideports = false;
|
||||||
|
|
||||||
log_header(design, "Executing BLIF frontend.\n");
|
log_header(design, "Executing BLIF frontend.\n");
|
||||||
|
|
||||||
|
@ -483,11 +610,15 @@ struct BlifFrontend : public Frontend {
|
||||||
sop_mode = true;
|
sop_mode = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (arg == "-wideports") {
|
||||||
|
wideports = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
extra_args(f, filename, args, argidx);
|
extra_args(f, filename, args, argidx);
|
||||||
|
|
||||||
parse_blif(design, *f, "", true, sop_mode);
|
parse_blif(design, *f, "", true, sop_mode, wideports);
|
||||||
}
|
}
|
||||||
} BlifFrontend;
|
} BlifFrontend;
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,8 @@
|
||||||
|
|
||||||
YOSYS_NAMESPACE_BEGIN
|
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
|
YOSYS_NAMESPACE_END
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
ilang_lexer.cc
|
ilang_lexer.cc
|
||||||
ilang_parser.output
|
ilang_parser.output
|
||||||
ilang_parser.tab.cc
|
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.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_parser.output
|
||||||
GENFILES += frontends/ilang/ilang_lexer.cc
|
GENFILES += frontends/ilang/ilang_lexer.cc
|
||||||
|
|
||||||
frontends/ilang/ilang_parser.tab.cc: frontends/ilang/ilang_parser.y
|
frontends/ilang/ilang_parser.tab.cc: frontends/ilang/ilang_parser.y
|
||||||
$(Q) mkdir -p $(dir $@)
|
$(Q) mkdir -p $(dir $@)
|
||||||
$(P) $(BISON) -d -r all -b frontends/ilang/ilang_parser $<
|
$(P) $(BISON) -o $@ -d -r all -b frontends/ilang/ilang_parser $<
|
||||||
$(Q) mv frontends/ilang/ilang_parser.tab.c frontends/ilang/ilang_parser.tab.cc
|
|
||||||
|
|
||||||
frontends/ilang/ilang_parser.tab.h: frontends/ilang/ilang_parser.tab.cc
|
frontends/ilang/ilang_parser.tab.hh: frontends/ilang/ilang_parser.tab.cc
|
||||||
|
|
||||||
frontends/ilang/ilang_lexer.cc: frontends/ilang/ilang_lexer.l
|
frontends/ilang/ilang_lexer.cc: frontends/ilang/ilang_lexer.l
|
||||||
$(Q) mkdir -p $(dir $@)
|
$(Q) mkdir -p $(dir $@)
|
||||||
|
|
|
@ -35,7 +35,7 @@ YOSYS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
struct IlangFrontend : public Frontend {
|
struct IlangFrontend : public Frontend {
|
||||||
IlangFrontend() : Frontend("ilang", "read modules from ilang file") { }
|
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---|
|
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||||
log("\n");
|
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("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("representation of a design in yosys's internal format.)\n");
|
||||||
log("\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");
|
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());
|
log("Input filename: %s\n", filename.c_str());
|
||||||
|
|
||||||
ILANG_FRONTEND::lexin = f;
|
ILANG_FRONTEND::lexin = f;
|
||||||
|
|
|
@ -32,6 +32,8 @@ YOSYS_NAMESPACE_BEGIN
|
||||||
namespace ILANG_FRONTEND {
|
namespace ILANG_FRONTEND {
|
||||||
extern std::istream *lexin;
|
extern std::istream *lexin;
|
||||||
extern RTLIL::Design *current_design;
|
extern RTLIL::Design *current_design;
|
||||||
|
extern bool flag_nooverwrite;
|
||||||
|
extern bool flag_overwrite;
|
||||||
}
|
}
|
||||||
|
|
||||||
YOSYS_NAMESPACE_END
|
YOSYS_NAMESPACE_END
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "frontends/ilang/ilang_frontend.h"
|
#include "frontends/ilang/ilang_frontend.h"
|
||||||
#include "ilang_parser.tab.h"
|
#include "ilang_parser.tab.hh"
|
||||||
|
|
||||||
USING_YOSYS_NAMESPACE
|
USING_YOSYS_NAMESPACE
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,8 @@ namespace ILANG_FRONTEND {
|
||||||
std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack;
|
std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack;
|
||||||
std::vector<RTLIL::CaseRule*> case_stack;
|
std::vector<RTLIL::CaseRule*> case_stack;
|
||||||
dict<RTLIL::IdString, RTLIL::Const> attrbuf;
|
dict<RTLIL::IdString, RTLIL::Const> attrbuf;
|
||||||
|
bool flag_nooverwrite, flag_overwrite;
|
||||||
|
bool delete_current_module;
|
||||||
}
|
}
|
||||||
using namespace ILANG_FRONTEND;
|
using namespace ILANG_FRONTEND;
|
||||||
YOSYS_NAMESPACE_END
|
YOSYS_NAMESPACE_END
|
||||||
|
@ -93,11 +95,26 @@ design:
|
||||||
|
|
||||||
module:
|
module:
|
||||||
TOK_MODULE TOK_ID EOL {
|
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());
|
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 = new RTLIL::Module;
|
||||||
current_module->name = $2;
|
current_module->name = $2;
|
||||||
current_module->attributes = attrbuf;
|
current_module->attributes = attrbuf;
|
||||||
|
if (!delete_current_module)
|
||||||
current_design->add(current_module);
|
current_design->add(current_module);
|
||||||
attrbuf.clear();
|
attrbuf.clear();
|
||||||
free($2);
|
free($2);
|
||||||
|
@ -105,6 +122,9 @@ module:
|
||||||
if (attrbuf.size() != 0)
|
if (attrbuf.size() != 0)
|
||||||
rtlil_frontend_ilang_yyerror("dangling attribute");
|
rtlil_frontend_ilang_yyerror("dangling attribute");
|
||||||
current_module->fixup_ports();
|
current_module->fixup_ports();
|
||||||
|
if (delete_current_module)
|
||||||
|
delete current_module;
|
||||||
|
current_module = nullptr;
|
||||||
} EOL;
|
} EOL;
|
||||||
|
|
||||||
module_body:
|
module_body:
|
||||||
|
@ -387,17 +407,13 @@ sigspec:
|
||||||
$$ = new RTLIL::SigSpec(current_module->wires_[$1]);
|
$$ = new RTLIL::SigSpec(current_module->wires_[$1]);
|
||||||
free($1);
|
free($1);
|
||||||
} |
|
} |
|
||||||
TOK_ID '[' TOK_INT ']' {
|
sigspec '[' TOK_INT ']' {
|
||||||
if (current_module->wires_.count($1) == 0)
|
$$ = new RTLIL::SigSpec($1->extract($3));
|
||||||
rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str());
|
delete $1;
|
||||||
$$ = new RTLIL::SigSpec(current_module->wires_[$1], $3);
|
|
||||||
free($1);
|
|
||||||
} |
|
} |
|
||||||
TOK_ID '[' TOK_INT ':' TOK_INT ']' {
|
sigspec '[' TOK_INT ':' TOK_INT ']' {
|
||||||
if (current_module->wires_.count($1) == 0)
|
$$ = new RTLIL::SigSpec($1->extract($5, $3 - $5 + 1));
|
||||||
rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str());
|
delete $1;
|
||||||
$$ = new RTLIL::SigSpec(current_module->wires_[$1], $5, $3 - $5 + 1);
|
|
||||||
free($1);
|
|
||||||
} |
|
} |
|
||||||
'{' sigspec_list '}' {
|
'{' sigspec_list '}' {
|
||||||
$$ = $2;
|
$$ = $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