summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format13
-rw-r--r--.dockerignore13
-rw-r--r--.gitignore3
-rw-r--r--.travis.yml50
-rwxr-xr-x.travis/setup.sh2
-rw-r--r--Brewfile1
-rw-r--r--CHANGELOG118
-rw-r--r--COPYING2
-rw-r--r--Dockerfile33
-rw-r--r--Makefile143
-rw-r--r--README.md70
-rw-r--r--backends/aiger/aiger.cc4
-rw-r--r--backends/blif/blif.cc32
-rw-r--r--backends/btor/btor.cc40
-rw-r--r--backends/edif/edif.cc6
-rw-r--r--backends/firrtl/firrtl.cc251
-rw-r--r--backends/ilang/ilang_backend.cc11
-rw-r--r--backends/intersynth/intersynth.cc2
-rw-r--r--backends/json/json.cc9
-rw-r--r--backends/protobuf/protobuf.cc1
-rw-r--r--backends/smt2/smt2.cc2
-rw-r--r--backends/smt2/smtio.py7
-rw-r--r--backends/smv/smv.cc2
-rw-r--r--backends/spice/spice.cc2
-rw-r--r--backends/table/table.cc2
-rw-r--r--backends/verilog/verilog_backend.cc154
-rw-r--r--examples/python-api/.gitignore1
-rwxr-xr-xexamples/python-api/pass.py32
-rwxr-xr-xexamples/python-api/script.py22
-rw-r--r--frontends/aiger/aigerparse.cc691
-rw-r--r--frontends/aiger/aigerparse.h1
-rw-r--r--frontends/ast/ast.cc118
-rw-r--r--frontends/ast/ast.h13
-rw-r--r--frontends/ast/genrtlil.cc95
-rw-r--r--frontends/ast/simplify.cc50
-rw-r--r--frontends/ilang/ilang_frontend.cc10
-rw-r--r--frontends/ilang/ilang_frontend.h1
-rw-r--r--frontends/ilang/ilang_lexer.l1
-rw-r--r--frontends/ilang/ilang_parser.y39
-rw-r--r--frontends/json/jsonparse.cc24
-rw-r--r--frontends/verific/verific.cc106
-rw-r--r--frontends/verific/verific.h2
-rw-r--r--frontends/verilog/Makefile.inc2
-rw-r--r--frontends/verilog/const2ast.cc28
-rw-r--r--frontends/verilog/verilog_frontend.cc43
-rw-r--r--frontends/verilog/verilog_frontend.h3
-rw-r--r--frontends/verilog/verilog_lexer.l32
-rw-r--r--frontends/verilog/verilog_parser.y530
-rw-r--r--kernel/cellaigs.cc2
-rw-r--r--kernel/celltypes.h7
-rw-r--r--kernel/cost.h4
-rw-r--r--kernel/driver.cc20
-rw-r--r--kernel/log.cc29
-rw-r--r--kernel/log.h44
-rw-r--r--kernel/register.cc2
-rw-r--r--kernel/rtlil.cc243
-rw-r--r--kernel/rtlil.h102
-rw-r--r--kernel/yosys.cc64
-rw-r--r--kernel/yosys.h20
-rw-r--r--libs/subcircuit/subcircuit.cc10
-rw-r--r--manual/CHAPTER_CellLib.tex4
-rw-r--r--manual/CHAPTER_Overview.tex17
-rw-r--r--misc/__init__.py5
-rw-r--r--misc/launcher.c3
-rw-r--r--misc/py_wrap_generator.py2097
-rw-r--r--passes/cmds/add.cc2
-rw-r--r--passes/cmds/blackbox.cc2
-rw-r--r--passes/cmds/bugpoint.cc14
-rw-r--r--passes/cmds/cover.cc10
-rw-r--r--passes/cmds/plugin.cc50
-rw-r--r--passes/cmds/qwp.cc2
-rw-r--r--passes/cmds/setattr.cc39
-rw-r--r--passes/cmds/setundef.cc118
-rw-r--r--passes/cmds/show.cc27
-rw-r--r--passes/cmds/stat.cc76
-rw-r--r--passes/cmds/tee.cc5
-rw-r--r--passes/cmds/trace.cc34
-rw-r--r--passes/cmds/write_file.cc2
-rw-r--r--passes/equiv/equiv_opt.cc21
-rw-r--r--passes/hierarchy/hierarchy.cc313
-rw-r--r--passes/hierarchy/uniquify.cc2
-rw-r--r--passes/memory/memory_bram.cc5
-rw-r--r--passes/memory/memory_dff.cc20
-rw-r--r--passes/opt/Makefile.inc2
-rw-r--r--passes/opt/muxpack.cc368
-rw-r--r--passes/opt/opt_clean.cc212
-rw-r--r--passes/opt/opt_expr.cc127
-rw-r--r--passes/opt/opt_lut.cc8
-rw-r--r--passes/opt/opt_merge.cc8
-rw-r--r--passes/opt/opt_muxtree.cc20
-rw-r--r--passes/opt/opt_rmdff.cc72
-rw-r--r--passes/opt/pmux2shiftx.cc860
-rw-r--r--passes/opt/rmports.cc2
-rw-r--r--passes/opt/wreduce.cc50
-rw-r--r--passes/pmgen/.gitignore1
-rw-r--r--passes/pmgen/Makefile.inc17
-rw-r--r--passes/pmgen/README.md32
-rw-r--r--passes/pmgen/ice40_dsp.cc117
-rw-r--r--passes/pmgen/ice40_dsp.pmg2
-rw-r--r--passes/pmgen/peepopt.cc68
-rw-r--r--passes/pmgen/peepopt_muldiv.pmg36
-rw-r--r--passes/pmgen/peepopt_shiftmul.pmg94
-rw-r--r--passes/pmgen/pmgen.py263
-rw-r--r--passes/proc/proc_mux.cc76
-rw-r--r--passes/proc/proc_rmdead.cc18
-rw-r--r--passes/sat/assertpmux.cc6
-rw-r--r--passes/sat/cutpoint.cc2
-rw-r--r--passes/sat/expose.cc2
-rw-r--r--passes/sat/fmcombine.cc45
-rw-r--r--passes/sat/miter.cc8
-rw-r--r--passes/sat/mutate.cc32
-rw-r--r--passes/sat/sat.cc5
-rw-r--r--passes/sat/sim.cc5
-rw-r--r--passes/techmap/abc.cc74
-rw-r--r--passes/techmap/attrmap.cc4
-rw-r--r--passes/techmap/dffinit.cc3
-rw-r--r--passes/techmap/dfflibmap.cc2
-rw-r--r--passes/techmap/flowmap.cc14
-rw-r--r--passes/techmap/libparse.cc2
-rw-r--r--passes/techmap/muxcover.cc227
-rw-r--r--passes/techmap/pmuxtree.cc4
-rw-r--r--passes/techmap/shregmap.cc197
-rw-r--r--passes/techmap/simplemap.cc2
-rw-r--r--passes/techmap/techmap.cc72
-rw-r--r--passes/techmap/zinit.cc2
-rw-r--r--techlibs/anlogic/anlogic_determine_init.cc4
-rw-r--r--techlibs/anlogic/anlogic_eqn.cc4
-rw-r--r--techlibs/common/prep.cc6
-rw-r--r--techlibs/common/simlib.v176
-rw-r--r--techlibs/common/synth.cc2
-rw-r--r--techlibs/ecp5/arith_map.v9
-rw-r--r--techlibs/ecp5/cells_map.v53
-rw-r--r--techlibs/ecp5/cells_sim.v94
-rw-r--r--techlibs/ecp5/synth_ecp5.cc14
-rw-r--r--techlibs/gowin/Makefile.inc10
-rw-r--r--techlibs/gowin/bram.txt29
-rw-r--r--techlibs/gowin/brams_init3.vh12
-rw-r--r--techlibs/gowin/brams_map.v103
-rw-r--r--techlibs/gowin/cells_map.v6
-rw-r--r--techlibs/gowin/cells_sim.v134
-rw-r--r--techlibs/gowin/determine_init.cc72
-rw-r--r--techlibs/gowin/dram.txt17
-rw-r--r--techlibs/gowin/drams_map.v31
-rw-r--r--techlibs/gowin/synth_gowin.cc56
-rw-r--r--techlibs/ice40/cells_sim.v64
-rw-r--r--techlibs/ice40/ice40_unlut.cc2
-rw-r--r--techlibs/ice40/synth_ice40.cc12
-rw-r--r--techlibs/intel/cyclonev/cells_sim.v12
-rw-r--r--techlibs/intel/synth_intel.cc433
-rw-r--r--techlibs/xilinx/cells_map.v138
-rw-r--r--techlibs/xilinx/cells_sim.v113
-rw-r--r--techlibs/xilinx/cells_xtra.sh14
-rw-r--r--techlibs/xilinx/cells_xtra.v72
-rw-r--r--techlibs/xilinx/drams.txt24
-rw-r--r--techlibs/xilinx/drams_map.v34
-rw-r--r--techlibs/xilinx/synth_xilinx.cc291
-rw-r--r--tests/aiger/.gitignore1
-rw-r--r--tests/aiger/and.aig3
-rw-r--r--tests/aiger/and_.aag (renamed from tests/aiger/and.aag)3
-rw-r--r--tests/aiger/and_.aig5
-rw-r--r--tests/aiger/buffer.aag2
-rw-r--r--tests/aiger/buffer.aig2
-rw-r--r--tests/aiger/cnt1.aag1
-rw-r--r--tests/aiger/cnt1.aig1
-rw-r--r--tests/aiger/cnt1e.aag1
-rw-r--r--tests/aiger/cnt1e.aig3
-rw-r--r--tests/aiger/false.aag1
-rw-r--r--tests/aiger/false.aig1
-rw-r--r--tests/aiger/inverter.aag2
-rw-r--r--tests/aiger/inverter.aig2
-rw-r--r--tests/aiger/notcnt1e.aag1
-rw-r--r--tests/aiger/notcnt1e.aig3
-rw-r--r--tests/aiger/or.aig3
-rw-r--r--tests/aiger/or_.aag (renamed from tests/aiger/or.aag)3
-rw-r--r--tests/aiger/or_.aig5
-rwxr-xr-xtests/aiger/run-test.sh56
-rw-r--r--tests/aiger/toggle.aag2
-rw-r--r--tests/aiger/toggle.aig2
-rw-r--r--tests/aiger/true.aag1
-rw-r--r--tests/aiger/true.aig1
-rwxr-xr-xtests/arch/run-test.sh18
-rw-r--r--tests/memories/firrtl_938.v22
-rw-r--r--tests/memories/issue00335.v28
-rw-r--r--tests/memories/issue00710.v17
-rw-r--r--tests/memories/read_two_mux.v16
-rwxr-xr-xtests/memories/run-test.sh16
-rw-r--r--tests/sat/counters-repeat.v38
-rw-r--r--tests/sat/counters-repeat.ys10
-rw-r--r--tests/simple/arrays02.sv16
-rw-r--r--tests/simple/attrib01_module.v21
-rw-r--r--tests/simple/attrib02_port_decl.v25
-rw-r--r--tests/simple/attrib03_parameter.v28
-rw-r--r--tests/simple/attrib04_net_var.v32
-rw-r--r--tests/simple/attrib05_port_conn.v.DISABLED21
-rw-r--r--tests/simple/attrib06_operator_suffix.v23
-rw-r--r--tests/simple/attrib07_func_call.v.DISABLED21
-rw-r--r--tests/simple/attrib08_mod_inst.v22
-rw-r--r--tests/simple/attrib09_case.v26
-rw-r--r--tests/simple/defvalue.sv22
-rw-r--r--tests/simple/dff_init.v12
-rw-r--r--tests/simple/forloops.v25
-rw-r--r--tests/simple/generate.v11
-rw-r--r--tests/simple/implicit_ports.v16
-rw-r--r--tests/simple/localparam_attr.v11
-rw-r--r--tests/simple/mem2reg.v22
-rw-r--r--tests/simple/param_attr.v11
-rw-r--r--tests/simple/peepopt.v13
-rw-r--r--tests/simple/retime.v6
-rwxr-xr-xtests/simple/run-test.sh3
-rw-r--r--tests/simple/wandwor.v36
-rw-r--r--tests/simple/xfirrtl1
-rwxr-xr-xtests/svinterfaces/runone.sh8
-rwxr-xr-xtests/tools/autotest.sh51
-rw-r--r--tests/various/.gitignore3
-rw-r--r--tests/various/attrib05_port_conn.v21
-rw-r--r--tests/various/attrib05_port_conn.ys2
-rw-r--r--tests/various/attrib07_func_call.v21
-rw-r--r--tests/various/attrib07_func_call.ys2
-rw-r--r--tests/various/chparam.sh52
-rw-r--r--tests/various/elab_sys_tasks.sv30
-rw-r--r--tests/various/elab_sys_tasks.ys1
-rw-r--r--tests/various/hierarchy.sh1
-rw-r--r--tests/various/muxcover.ys461
-rw-r--r--tests/various/muxpack.v259
-rw-r--r--tests/various/muxpack.ys268
-rw-r--r--tests/various/opt_rmdff.v50
-rw-r--r--tests/various/opt_rmdff.ys26
-rw-r--r--tests/various/pmux2shiftx.v44
-rw-r--r--tests/various/pmux2shiftx.ys39
-rw-r--r--tests/various/shregmap.v48
-rw-r--r--tests/various/shregmap.ys66
-rw-r--r--tests/various/signext.ys33
-rw-r--r--tests/various/specify.v39
-rw-r--r--tests/various/specify.ys58
234 files changed, 11867 insertions, 1844 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 00000000..28d13da2
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,13 @@
+# Default Linux style
+BasedOnStyle: LLVM
+IndentWidth: 8
+UseTab: Always
+BreakBeforeBraces: Linux
+AllowShortIfStatementsOnASingleLine: false
+IndentCaseLabels: false
+
+# From CodingReadme
+TabWidth: 8
+ContinuationIndentWidth: 2
+ColumnLimit: 150
+# BreakBeforeBraces: Linux
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..9910e995
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,13 @@
+.editorconfig
+.gitignore
+.gitmodules
+.github
+.git
+Dockerfile
+README.md
+manual
+CodingReadme
+CodeOfConduct
+.travis
+.travis.yml
+
diff --git a/.gitignore b/.gitignore
index e24f7975..76f53cd0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
*.gch
*.gcda
*.gcno
+__pycache__
/.cproject
/.project
/.settings
@@ -28,6 +29,8 @@
/yosys-smtbmc-script.py
/yosys-filterlib
/yosys-filterlib.exe
+/kernel/*.pyh
+/kernel/python_wrappers.cc
/kernel/version_*.cc
/share
/yosys-win32-mxebin-*
diff --git a/.travis.yml b/.travis.yml
index 7c6e4e43..c253b2ff 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -32,33 +32,13 @@ matrix:
- xdot
- pkg-config
- python
+ - python3
+ - libboost-system-dev
+ - libboost-python-dev
+ - libboost-filesystem-dev
env:
- MATRIX_EVAL="CONFIG=gcc && CC=gcc-4.8 && CXX=g++-4.8"
- # Latest gcc-6 on Travis Linux
- - os: linux
- addons:
- apt:
- sources:
- - ubuntu-toolchain-r-test
- packages:
- - g++-6
- - gperf
- - build-essential
- - bison
- - flex
- - libreadline-dev
- - gawk
- - tcl-dev
- - libffi-dev
- - git
- - graphviz
- - xdot
- - pkg-config
- - python
- env:
- - MATRIX_EVAL="CONFIG=gcc && CC=gcc-6 && CXX=g++-6"
-
# Latest gcc supported on Travis Linux
- os: linux
addons:
@@ -66,7 +46,7 @@ matrix:
sources:
- ubuntu-toolchain-r-test
packages:
- - g++-7
+ - g++-9
- gperf
- build-essential
- bison
@@ -80,8 +60,12 @@ matrix:
- xdot
- pkg-config
- python
+ - python3
+ - libboost-system-dev
+ - libboost-python-dev
+ - libboost-filesystem-dev
env:
- - MATRIX_EVAL="CONFIG=gcc && CC=gcc-7 && CXX=g++-7"
+ - MATRIX_EVAL="CONFIG=gcc && CC=gcc-9 && CXX=g++-9"
# Clang which ships on Trusty Linux
- os: linux
@@ -105,6 +89,10 @@ matrix:
- xdot
- pkg-config
- python
+ - python3
+ - libboost-system-dev
+ - libboost-python-dev
+ - libboost-filesystem-dev
env:
- MATRIX_EVAL="CONFIG=clang && CC=clang-3.8 && CXX=clang++-3.8"
@@ -113,9 +101,9 @@ matrix:
addons:
apt:
sources:
- - llvm-toolchain-trusty-5.0
+ - llvm-toolchain-xenial-8
packages:
- - clang-5.0
+ - clang-8
- gperf
- build-essential
- bison
@@ -129,8 +117,12 @@ matrix:
- xdot
- pkg-config
- python
+ - python3
+ - libboost-system-dev
+ - libboost-python-dev
+ - libboost-filesystem-dev
env:
- - MATRIX_EVAL="CONFIG=clang && CC=clang-5.0 && CXX=clang++-5.0"
+ - MATRIX_EVAL="CONFIG=clang && CC=clang-8 && CXX=clang++-8"
# # Latest clang on Mac OS X
# - os: osx
diff --git a/.travis/setup.sh b/.travis/setup.sh
index 4af0b8ee..02879b97 100755
--- a/.travis/setup.sh
+++ b/.travis/setup.sh
@@ -51,7 +51,7 @@ fi
git clone git://github.com/steveicarus/iverilog.git
cd iverilog
autoconf
- ./configure --prefix=$HOME/.local-bin
+ CC=gcc CXX=g++ ./configure --prefix=$HOME/.local-bin
make
make install
echo
diff --git a/Brewfile b/Brewfile
index 0c58ce16..4ffe50e8 100644
--- a/Brewfile
+++ b/Brewfile
@@ -6,3 +6,4 @@ brew "git"
brew "graphviz"
brew "pkg-config"
brew "python3"
+brew "tcl-tk"
diff --git a/CHANGELOG b/CHANGELOG
index 42e01645..afbcffa2 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,20 +3,124 @@ List of major changes and improvements between releases
=======================================================
-Yosys 0.8 .. Yosys 0.8-dev
+Yosys 0.8 .. Yosys 0.9
--------------------------
* Various
- - Added $changed support to read_verilog
+ - Many bugfixes and small improvements
+ - Added support for SystemVerilog interfaces and modports
- 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
+ - Added "flowmap" LUT mapping pass
+ - Added "rename -wire" to rename cells based on the wires they drive
+ - Added "bugpoint" for creating minimised testcases
+ - Added "write_edif -gndvccy"
+ - "write_verilog" to escape Verilog keywords
+ - Fixed sign handling of real constants
+ - "write_verilog" to write initial statement for initial flop state
+ - Added pmgen pattern matcher generator
+ - Fixed opt_rmdff handling of $_DFFSR_???_ and $_DLATCHSR_???_
+ - Added "setundef -params" to replace undefined cell parameters
+ - Renamed "yosys -D" to "yosys -U", added "yosys -D" to set Verilog defines
+ - Fixed handling of defparam when default_nettype is none
+ - Fixed "wreduce" flipflop handling
+ - Fixed FIRRTL to Verilog process instance subfield assignment
+ - Added "write_verilog -siminit"
+ - Several fixes and improvements for mem2reg memories
+ - Fixed handling of task output ports in clocked always blocks
+ - Improved handling of and-with-1 and or-with-0 in "opt_expr"
+ - Added "read_aiger" frontend
+ - Added "mutate" pass
+ - Added "hdlname" attribute
+ - Added "rename -output"
+ - Added "read_ilang -lib"
+ - Improved "proc" full_case detection and handling
+ - Added "whitebox" and "lib_whitebox" attributes
+ - Added "read_verilog -nowb", "flatten -wb" and "wbflip"
+ - Added Python bindings and support for Python plug-ins
+ - Added "pmux2shiftx"
+ - Added log_debug framework for reduced default verbosity
+ - Improved "opt_expr" and "opt_clean" handling of (partially) undriven and/or unused wires
+ - Added "peepopt" peephole optimisation pass using pmgen
+ - Added approximate support for SystemVerilog "var" keyword
+ - Added parsing of "specify" blocks into $specrule and $specify[23]
+ - Added support for attributes on parameters and localparams
+ - Added support for parsing attributes on port connections
+ - Added "wreduce -keepdc"
+ - Added support for optimising $dffe and $_DFFE_* cells in "opt_rmdff"
+ - Added Verilog wand/wor wire type support
+ - Added support for elaboration system tasks
+ - Added "muxcover -mux{4,8,16}=<cost>"
+ - Added "muxcover -dmux=<cost>"
+ - Added "muxcover -nopartial"
+ - Added "muxpack" pass
+ - Added "pmux2shiftx -norange"
+ - Added support for "~" in filename parsing
+ - Added "read_verilog -pwires" feature to turn parameters into wires
+ - Fixed sign extension of unsized constants with 'bx and 'bz MSB
+ - Fixed genvar to be a signed type
+ - Added support for attributes on case rules
+ - Added "upto" and "offset" to JSON frontend and backend
+ - Several liberty file parser improvements
+ - Fixed handling of more complex BRAM patterns
+ - Add "write_aiger -I -O -B"
+
+ * Formal Verification
+ - Added $changed support to read_verilog
+ - Added "read_verilog -noassert -noassume -assert-assumes"
+ - Added btor ops for $mul, $div, $mod and $concat
+ - Added yosys-smtbmc support for btor witnesses
+ - Added "supercover" pass
+ - Fixed $global_clock handling vs autowire
+ - Added $dffsr support to "async2sync"
+ - Added "fmcombine" pass
+ - Added memory init support in "write_btor"
+ - Added "cutpoint" pass
+ - Changed "ne" to "neq" in btor2 output
+ - Added support for SVA "final" keyword
+ - Added "fmcombine -initeq -anyeq"
+ - Added timescale and generated-by header to yosys-smtbmc vcd output
+ - Improved BTOR2 handling of undriven wires
+ * Verific support
+ - Enabled Verific flags vhdl_support_variable_slice and veri_elaborate_top_level_modules_having_interface_ports
+ - Improved support for asymmetric memories
+ - Added "verific -chparam"
+ - Fixed "verific -extnets" for more complex situations
+ - Added "read -verific" and "read -noverific"
+ - Added "hierarchy -chparam"
+
+ * New back-ends
+ - Added initial Anlogic support
+ - Added initial SmartFusion2 and IGLOO2 support
+
+ * ECP5 support
+ - Added "synth_ecp5 -nowidelut"
+ - Added BRAM inference support to "synth_ecp5"
+ - Added support for transforming Diamond IO and flipflop primitives
+
+ * iCE40 support
+ - Added "ice40_unlut" pass
+ - Added "synth_ice40 -relut"
+ - Added "synth_ice40 -noabc"
+ - Added "synth_ice40 -dffe_min_ce_use"
+ - Added DSP inference support using pmgen
+ - Added support for initialising BRAM primitives from a file
+ - Added iCE40 Ultra RGB LED driver cells
+
+ * Xilinx support
+ - Use "write_edif -pvector bra" for Xilinx EDIF files
+ - Fixes for VPR place and route support with "synth_xilinx"
+ - Added more cell simulation models
+ - Added "synth_xilinx -family"
+ - Added "stat -tech xilinx" to estimate logic cell usage
+ - Added "synth_xilinx -nocarry"
+ - Added "synth_xilinx -nowidelut"
+ - "synth_xilinx" to now infer hard shift registers (-nosrl to disable)
+ - Added support for mapping RAM32X1D
Yosys 0.7 .. Yosys 0.8
----------------------
@@ -29,7 +133,7 @@ Yosys 0.7 .. Yosys 0.8
- Added "write_verilog -decimal"
- Added "scc -set_attr"
- Added "verilog_defines" command
- - Remeber defines from one read_verilog to next
+ - Remember defines from one read_verilog to next
- Added support for hierarchical defparam
- Added FIRRTL back-end
- Improved ABC default scripts
@@ -38,7 +142,7 @@ Yosys 0.7 .. Yosys 0.8
- Added Verilog $rtoi and $itor support
- Added "check -initdrv"
- Added "read_blif -wideports"
- - Added support for systemVerilog "++" and "--" operators
+ - 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
diff --git a/COPYING b/COPYING
index a121cdfe..0839088c 100644
--- a/COPYING
+++ b/COPYING
@@ -1,4 +1,4 @@
-Copyright (C) 2012 - 2018 Clifford Wolf <clifford@clifford.at>
+Copyright (C) 2012 - 2019 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
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..3c7188d8
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,33 @@
+FROM ubuntu:18.04 as builder
+LABEL author="Abdelrahman Hosny <abdelrahman.hosny@hotmail.com>"
+ENV DEBIAN_FRONTEND=noninteractive
+RUN apt-get update && apt-get install -y build-essential \
+ clang \
+ bison \
+ flex \
+ libreadline-dev \
+ gawk \
+ tcl-dev \
+ libffi-dev \
+ git \
+ pkg-config \
+ python3 && \
+ rm -rf /var/lib/apt/lists
+COPY . /
+RUN make && \
+ make install
+
+FROM ubuntu:18.04
+ENV DEBIAN_FRONTEND=noninteractive
+RUN apt-get update && apt-get install -y libreadline-dev tcl-dev
+
+COPY --from=builder /yosys /build/yosys
+COPY --from=builder /yosys-abc /build/yosys-abc
+COPY --from=builder /yosys-config /build/yosys-config
+COPY --from=builder /yosys-filterlib /build/yosys-filterlib
+COPY --from=builder /yosys-smtbmc /build/yosys-smtbmc
+
+ENV PATH /build:$PATH
+RUN useradd -m yosys
+USER yosys
+ENTRYPOINT ["yosys"]
diff --git a/Makefile b/Makefile
index 8586e976..a6731541 100644
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,7 @@
CONFIG := clang
# CONFIG := gcc
# CONFIG := gcc-4.8
+# CONFIG := afl-gcc
# CONFIG := emcc
# CONFIG := mxe
# CONFIG := msys2
@@ -19,6 +20,9 @@ ENABLE_COVER := 1
ENABLE_LIBYOSYS := 0
ENABLE_PROTOBUF := 0
+# python wrappers
+ENABLE_PYOSYS := 0
+
# other configuration flags
ENABLE_GCOV := 0
ENABLE_GPROF := 0
@@ -42,6 +46,10 @@ OS := $(shell uname -s)
PREFIX ?= /usr/local
INSTALL_SUDO :=
+ifneq ($(wildcard Makefile.conf),)
+include Makefile.conf
+endif
+
BINDIR := $(PREFIX)/bin
LIBDIR := $(PREFIX)/lib
DATDIR := $(PREFIX)/share/yosys
@@ -81,6 +89,11 @@ PLUGIN_LDFLAGS += -undefined dynamic_lookup
# homebrew search paths
ifneq ($(shell which brew),)
BREW_PREFIX := $(shell brew --prefix)/opt
+$(info $$BREW_PREFIX is [${BREW_PREFIX}])
+ifeq ($(ENABLE_PYOSYS),1)
+CXXFLAGS += -I$(BREW_PREFIX)/boost/include/boost
+LDFLAGS += -L$(BREW_PREFIX)/boost/lib
+endif
CXXFLAGS += -I$(BREW_PREFIX)/readline/include
LDFLAGS += -L$(BREW_PREFIX)/readline/lib
PKG_CONFIG_PATH := $(BREW_PREFIX)/libffi/lib/pkgconfig:$(PKG_CONFIG_PATH)
@@ -101,7 +114,7 @@ LDFLAGS += -rdynamic
LDLIBS += -lrt
endif
-YOSYS_VER := 0.8+$(shell cd $(YOSYS_SRC) && test -e .git && { git log --author=clifford@clifford.at --oneline 4d4665b.. 2> /dev/null | wc -l; })
+YOSYS_VER := 0.9
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
OBJS = kernel/version_$(GIT_REV).o
@@ -111,7 +124,7 @@ OBJS = kernel/version_$(GIT_REV).o
# is just a symlink to your actual ABC working directory, as 'make mrproper'
# will remove the 'abc' directory and you do not want to accidentally
# delete your work on ABC..
-ABCREV = 2ddc57d
+ABCREV = 3709744
ABCPULL = 1
ABCURL ?= https://github.com/berkeley-abc/abc
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1
@@ -130,6 +143,21 @@ $(info $(subst $$--$$,$(newline),$(shell sed 's,^,[Makefile.conf] ,; s,$$,$$--$$
include Makefile.conf
endif
+ifeq ($(ENABLE_PYOSYS),1)
+PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)"
+PYTHON_EXECUTABLE := $(shell if python3 -c ""; then echo "python3"; else echo "python"; fi)
+PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"")
+PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.)
+PYTHON_PREFIX := $(shell $(PYTHON_EXECUTABLE)-config --prefix)
+PYTHON_DESTDIR := $(PYTHON_PREFIX)/lib/python$(PYTHON_VERSION)/site-packages
+
+# Reload Makefile.conf to override python specific variables if defined
+ifneq ($(wildcard Makefile.conf),)
+include Makefile.conf
+endif
+
+endif
+
ifeq ($(CONFIG),clang)
CXX = clang
LD = clang++
@@ -177,6 +205,12 @@ LD = gcc-4.8
CXXFLAGS += -std=c++11 -Os
ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
+else ifeq ($(CONFIG),afl-gcc)
+CXX = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc
+LD = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc
+CXXFLAGS += -std=c++11 -Os
+ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
+
else ifeq ($(CONFIG),cygwin)
CXX = gcc
LD = gcc
@@ -228,7 +262,8 @@ CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s
LDLIBS := $(filter-out -lrt,$(LDLIBS))
ABCMKARGS += ARCHFLAGS="-DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w"
-ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" ABC_USE_NO_READLINE=1 CC="/usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc"
+# TODO: Try to solve pthread linking issue in more appropriate way
+ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" LDFLAGS="-Wl,--allow-multiple-definition" ABC_USE_NO_READLINE=1 CC="/usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc"
EXE = .exe
else ifeq ($(CONFIG),msys2)
@@ -261,6 +296,55 @@ ifeq ($(ENABLE_LIBYOSYS),1)
TARGETS += libyosys.so
endif
+ifeq ($(ENABLE_PYOSYS),1)
+
+#Detect name of boost_python library. Some distros usbe boost_python-py<version>, other boost_python<version>, some only use the major version number, some a concatenation of major and minor version numbers
+ifeq ($(OS), Darwin)
+BOOST_PYTHON_LIB ?= $(shell \
+ if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))"; else \
+ if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
+ if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))"; else \
+ if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_EXECUTABLE)-config --ldflags) -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
+ echo ""; fi; fi; fi; fi;)
+else
+BOOST_PYTHON_LIB ?= $(shell \
+ if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))"; else \
+ if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
+ if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))"; else \
+ if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_EXECUTABLE)-config --libs` -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \
+ echo ""; fi; fi; fi; fi;)
+endif
+
+ifeq ($(BOOST_PYTHON_LIB),)
+$(error BOOST_PYTHON_LIB could not be detected. Please define manualy)
+endif
+
+ifeq ($(OS), Darwin)
+ifeq ($(PYTHON_MAJOR_VERSION),3)
+LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
+CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
+else
+LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
+CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
+endif
+else
+ifeq ($(PYTHON_MAJOR_VERSION),3)
+LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
+CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
+else
+LDLIBS += $(shell $(PYTHON_EXECUTABLE)-config --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem
+CXXFLAGS += $(shell $(PYTHON_EXECUTABLE)-config --includes) -DWITH_PYTHON
+endif
+endif
+
+ifeq ($(ENABLE_PYOSYS),1)
+PY_WRAPPER_FILE = kernel/python_wrappers
+OBJS += $(PY_WRAPPER_FILE).o
+PY_GEN_SCRIPT= py_wrap_generator
+PY_WRAP_INCLUDES := $(shell python$(PYTHON_VERSION) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).print_includes()")
+endif
+endif
+
ifeq ($(ENABLE_READLINE),1)
CXXFLAGS += -DYOSYS_ENABLE_READLINE
ifeq ($(OS), FreeBSD)
@@ -313,7 +397,7 @@ endif
ifeq ($(CONFIG),mxe)
CXXFLAGS += -DYOSYS_ENABLE_TCL
-LDLIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz
+LDLIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz -luserenv
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)
@@ -504,12 +588,26 @@ yosys$(EXE): $(OBJS)
$(P) $(LD) -o yosys$(EXE) $(LDFLAGS) $(OBJS) $(LDLIBS)
libyosys.so: $(filter-out kernel/driver.o,$(OBJS))
+ifeq ($(OS), Darwin)
+ $(P) $(LD) -o libyosys.so -shared -Wl,-install_name,libyosys.so $(LDFLAGS) $^ $(LDLIBS)
+else
$(P) $(LD) -o libyosys.so -shared -Wl,-soname,libyosys.so $(LDFLAGS) $^ $(LDLIBS)
+endif
%.o: %.cc
$(Q) mkdir -p $(dir $@)
$(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $<
+%.pyh: %.h
+ $(Q) mkdir -p $(dir $@)
+ $(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(LD) -x c++ -o $@ -E -P -
+
+ifeq ($(ENABLE_PYOSYS),1)
+$(PY_WRAPPER_FILE).cc: misc/$(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES)
+ $(Q) mkdir -p $(dir $@)
+ $(P) python$(PYTHON_VERSION) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).gen_wrappers(\"$(PY_WRAPPER_FILE).cc\")"
+endif
+
%.o: %.cpp
$(Q) mkdir -p $(dir $@)
$(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $<
@@ -571,6 +669,12 @@ else
SEEDOPT=""
endif
+ifneq ($(ABCEXTERNAL),)
+ABCOPT="-A $(ABCEXTERNAL)"
+else
+ABCOPT=""
+endif
+
test: $(TARGETS) $(EXTRA_TARGETS)
+cd tests/simple && bash run-test.sh $(SEEDOPT)
+cd tests/hana && bash run-test.sh $(SEEDOPT)
@@ -579,13 +683,14 @@ test: $(TARGETS) $(EXTRA_TARGETS)
+cd tests/share && bash run-test.sh $(SEEDOPT)
+cd tests/fsm && bash run-test.sh $(SEEDOPT)
+cd tests/techmap && bash run-test.sh
- +cd tests/memories && bash run-test.sh $(SEEDOPT)
+ +cd tests/memories && bash run-test.sh $(ABCOPT) $(SEEDOPT)
+cd tests/bram && bash run-test.sh $(SEEDOPT)
+cd tests/various && bash run-test.sh
+cd tests/sat && bash run-test.sh
+cd tests/svinterfaces && bash run-test.sh $(SEEDOPT)
+cd tests/opt && bash run-test.sh
- +cd tests/aiger && bash run-test.sh
+ +cd tests/aiger && bash run-test.sh $(ABCOPT)
+ +cd tests/arch && bash run-test.sh
@echo ""
@echo " Passed \"make test\"."
@echo ""
@@ -606,7 +711,7 @@ vloghtb: $(TARGETS) $(EXTRA_TARGETS)
ystests: $(TARGETS) $(EXTRA_TARGETS)
rm -rf tests/ystests
- git clone https://github.com/YosysHQ/yosys-tests.git tests/ystests
+ git clone -b yosys-0.9-rc https://github.com/YosysHQ/yosys-tests.git tests/ystests
+$(MAKE) PATH="$$PWD:$$PATH" -C tests/ystests
@echo ""
@echo " Finished \"make ystests\"."
@@ -635,9 +740,14 @@ endif
$(INSTALL_SUDO) mkdir -p $(DESTDIR)$(DATDIR)
$(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(DATDIR)/.
ifeq ($(ENABLE_LIBYOSYS),1)
- $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)
+ $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(LIBDIR)
+ $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)/
$(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so
- $(INSTALL_SUDO) ldconfig
+ifeq ($(ENABLE_PYOSYS),1)
+ $(INSTALL_SUDO) mkdir -p $(PYTHON_DESTDIR)/pyosys
+ $(INSTALL_SUDO) cp libyosys.so $(PYTHON_DESTDIR)/pyosys/
+ $(INSTALL_SUDO) cp misc/__init__.py $(PYTHON_DESTDIR)/pyosys/
+endif
endif
uninstall:
@@ -645,6 +755,11 @@ uninstall:
$(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR)
ifeq ($(ENABLE_LIBYOSYS),1)
$(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so
+ifeq ($(ENABLE_PYOSYS),1)
+ $(INSTALL_SUDO) rm -vf $(PYTHON_DESTDIR)/pyosys/libyosys.so
+ $(INSTALL_SUDO) rm -vf $(PYTHON_DESTDIR)/pyosys/__init__.py
+ $(INSTALL_SUDO) rmdir $(PYTHON_DESTDIR)/pyosys
+endif
endif
update-manual: $(TARGETS) $(EXTRA_TARGETS)
@@ -657,8 +772,9 @@ manual: $(TARGETS) $(EXTRA_TARGETS)
clean:
rm -rf share
+ rm -rf kernel/*.pyh
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) $(PY_WRAP_INCLUDES) $(PY_WRAPPER_FILE).cc
rm -f kernel/version_*.o kernel/version_*.cc abc/abc-[0-9a-f]* abc/libabc-[0-9a-f]*.a
rm -f libs/*/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d techlibs/*/*.d
rm -rf tests/asicworld/*.out tests/asicworld/*.log
@@ -732,6 +848,9 @@ config-gcc-static: clean
config-gcc-4.8: clean
echo 'CONFIG := gcc-4.8' > Makefile.conf
+config-afl-gcc: clean
+ echo 'CONFIG := afl-gcc' > Makefile.conf
+
config-emcc: clean
echo 'CONFIG := emcc' > Makefile.conf
echo 'ENABLE_TCL := 0' >> Makefile.conf
@@ -745,9 +864,11 @@ config-mxe: clean
config-msys2: clean
echo 'CONFIG := msys2' > Makefile.conf
+ echo 'ENABLE_PLUGINS := 0' >> Makefile.conf
config-msys2-64: clean
echo 'CONFIG := msys2-64' > Makefile.conf
+ echo 'ENABLE_PLUGINS := 0' >> Makefile.conf
config-cygwin: clean
echo 'CONFIG := cygwin' > Makefile.conf
@@ -778,5 +899,5 @@ echo-git-rev:
-include techlibs/*/*.d
.PHONY: all top-all abc test install install-abc manual clean mrproper qtcreator coverage vcxsrc mxebin
-.PHONY: config-clean config-clang config-gcc config-gcc-static config-gcc-4.8 config-gprof config-sudo
+.PHONY: config-clean config-clang config-gcc config-gcc-static config-gcc-4.8 config-afl-gcc config-gprof config-sudo
diff --git a/README.md b/README.md
index 4048ecbc..5132332a 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
```
yosys -- Yosys Open SYnthesis Suite
-Copyright (C) 2012 - 2018 Clifford Wolf <clifford@clifford.at>
+Copyright (C) 2012 - 2019 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
@@ -66,25 +66,29 @@ 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
+ graphviz xdot pkg-config python3 libboost-system-dev \
+ libboost-python-dev libboost-filesystem-dev
-Similarily, on Mac OS X MacPorts or Homebrew can be used to install dependencies:
+Similarily, on Mac OS X Homebrew can be used to install dependencies:
$ brew tap Homebrew/bundle && brew bundle
+
+or MacPorts:
+
$ sudo port install bison flex readline gawk libffi \
- git graphviz pkgconfig python36
+ git graphviz pkgconfig python36 boost tcl
On FreeBSD use the following command to install all prerequisites:
# pkg install bison flex readline gawk libffi\
- git graphviz pkgconfig python3 python36 tcl-wrapper
+ git graphviz pkgconfig python3 python36 tcl-wrapper boost-libs
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
+ setup-x86_64.exe -q --packages=bison,flex,gcc-core,gcc-g++,git,libffi-devel,libreadline-devel,make,pkg-config,python3,tcl-devel,boost-build
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
@@ -256,13 +260,9 @@ 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 ``tri``, ``triand`` and ``trior`` net types
-- The ``disable``, ``primitive`` and ``specify`` statements
-
-- Latched logic (is synthesized as logic with feedback loops)
+- The ``config`` and ``disable`` keywords and library map files
Verilog Attributes and non-standard features
@@ -310,12 +310,24 @@ Verilog Attributes and non-standard features
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.
+ also does not output blackbox modules on default. ``read_verilog``, unless
+ called with ``-noblackbox`` will automatically set the blackbox attribute
+ on any empty module it reads.
+
+- The ``noblackbox`` attribute set on an empty module prevents ``read_verilog``
+ from automatically setting the blackbox attribute on the module.
-- The ``dynports'' attribute is used by the Verilog front-end to mark modules
+- The ``whitebox`` attribute on modules triggers the same behavior as
+ ``blackbox``, but is for whitebox modules, i.e. library modules that
+ contain a behavioral model of the cell type.
+
+- The ``lib_whitebox`` attribute overwrites ``whitebox`` when ``read_verilog``
+ is run in `-lib` mode. Otherwise it's automatically removed.
+
+- The ``dynports`` attribute is used by the Verilog front-end to mark modules
that have ports with a width that depends on a parameter.
-- The ``hdlname'' attribute is used by some passes to document the original
+- The ``hdlname`` attribute is used by some passes to document the original
(HDL) name of a module when renaming a module.
- The ``keep`` attribute on cells and wires is used to mark objects that should
@@ -341,6 +353,14 @@ Verilog Attributes and non-standard features
through the synthesis. When entities are combined, a new |-separated
string is created that contains all the string from the original entities.
+- The ``defaultvalue`` attribute is used to store default values for
+ module inputs. The attribute is attached to the input wire by the HDL
+ front-end when the input is declared with a default value.
+
+- The ``parameter`` and ``localparam`` attributes are used to mark wires
+ that represent module parameters or localparams (when the HDL front-end
+ is run in -pwires mode).
+
- 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
@@ -357,7 +377,7 @@ Verilog Attributes and non-standard features
- 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:
+ triple double quotes are removed from the macro body. For example:
`define MY_MACRO(a, b) """
assign a = 23;
@@ -404,12 +424,18 @@ Verilog Attributes and non-standard features
$ yosys -p 'plugin -a foo -i /lib/libm.so; read_verilog dpitest.v'
- Sized constants (the syntax ``<size>'s?[bodh]<value>``) support constant
- expressions as <size>. If the expression is not a simple identifier, it
+ expressions as ``<size>``. If the expression is not a simple identifier, it
must be put in parentheses. Examples: ``WIDTH'd42``, ``(4+2)'b101010``
-- The system tasks ``$finish`` and ``$display`` are supported in initial blocks
- in an unconditional context (only if/case statements on parameters
- and constant values). The intended use for this is synthesis-time DRC.
+- The system tasks ``$finish``, ``$stop`` and ``$display`` are supported in
+ initial blocks in an unconditional context (only if/case statements on
+ expressions over parameters and constant values are allowed). The intended
+ use for this is synthesis-time DRC.
+
+- There is limited support for converting specify .. endspecify statements to
+ special ``$specify2``, ``$specify3``, and ``$specrule`` cells, for use in
+ blackboxes and whiteboxes. Use ``read_verilog -specify`` to enable this
+ functionality. (By default specify .. endspecify blocks are ignored.)
Non-standard or SystemVerilog features for formal verification
@@ -444,7 +470,7 @@ Non-standard or SystemVerilog features for formal verification
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
+ 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.
@@ -457,7 +483,7 @@ 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.
+ always block: ``assert(<expression>);``. It is transformed to an ``$assert`` cell.
- The ``assume``, ``restrict``, and ``cover`` statements from SystemVerilog are
also supported. The same limitations as with the ``assert`` statement apply.
diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc
index dfe506c6..6863b40f 100644
--- a/backends/aiger/aiger.cc
+++ b/backends/aiger/aiger.cc
@@ -89,7 +89,8 @@ struct AigerWriter
aig_map[bit] = mkgate(a0, a1);
} else
if (alias_map.count(bit)) {
- aig_map[bit] = bit2aig(alias_map.at(bit));
+ int a = bit2aig(alias_map.at(bit));
+ aig_map[bit] = a;
}
if (bit == State::Sx || bit == State::Sz)
@@ -776,6 +777,7 @@ struct AigerBackend : public Backend {
writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode);
if (!map_filename.empty()) {
+ rewrite_filename(filename);
std::ofstream mapf;
mapf.open(map_filename.c_str(), std::ofstream::trunc);
if (mapf.fail())
diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc
index 0db5ff27..a1761b66 100644
--- a/backends/blif/blif.cc
+++ b/backends/blif/blif.cc
@@ -140,7 +140,7 @@ struct BlifDumper
return "subckt";
if (!design->modules_.count(RTLIL::escape_id(cell_type)))
return "gate";
- if (design->modules_.at(RTLIL::escape_id(cell_type))->get_bool_attribute("\\blackbox"))
+ if (design->modules_.at(RTLIL::escape_id(cell_type))->get_blackbox_attribute())
return "gate";
return "subckt";
}
@@ -196,7 +196,7 @@ struct BlifDumper
}
f << stringf("\n");
- if (module->get_bool_attribute("\\blackbox")) {
+ if (module->get_blackbox_attribute()) {
f << stringf(".blackbox\n");
f << stringf(".end\n");
return;
@@ -409,12 +409,26 @@ struct BlifDumper
f << stringf(".%s %s", subckt_or_gate(cell->type.str()), cstr(cell->type));
for (auto &conn : cell->connections())
- for (int i = 0; i < conn.second.size(); i++) {
- if (conn.second.size() == 1)
- f << stringf(" %s", cstr(conn.first));
- else
- f << stringf(" %s[%d]", cstr(conn.first), i);
- f << stringf("=%s", cstr(conn.second.extract(i, 1)));
+ {
+ if (conn.second.size() == 1) {
+ f << stringf(" %s=%s", cstr(conn.first), cstr(conn.second[0]));
+ continue;
+ }
+
+ Module *m = design->module(cell->type);
+ Wire *w = m ? m->wire(conn.first) : nullptr;
+
+ if (w == nullptr) {
+ for (int i = 0; i < GetSize(conn.second); i++)
+ f << stringf(" %s[%d]=%s", cstr(conn.first), i, cstr(conn.second[i]));
+ } else {
+ for (int i = 0; i < std::min(GetSize(conn.second), GetSize(w)); i++) {
+ SigBit sig(w, i);
+ f << stringf(" %s[%d]=%s", cstr(conn.first), sig.wire->upto ?
+ sig.wire->start_offset+sig.wire->width-sig.offset-1 :
+ sig.wire->start_offset+sig.offset, cstr(conn.second[i]));
+ }
+ }
}
f << stringf("\n");
@@ -640,7 +654,7 @@ struct BlifBackend : public Backend {
for (auto module_it : design->modules_)
{
RTLIL::Module *module = module_it.second;
- if (module->get_bool_attribute("\\blackbox") && !config.blackbox_mode)
+ if (module->get_blackbox_attribute() && !config.blackbox_mode)
continue;
if (module->processes.size() != 0)
diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc
index 55c49499..a507b120 100644
--- a/backends/btor/btor.cc
+++ b/backends/btor/btor.cc
@@ -17,6 +17,11 @@
*
*/
+// [[CITE]] Btor2 , BtorMC and Boolector 3.0
+// Aina Niemetz, Mathias Preiner, Clifford Wolf, Armin Biere
+// Computer Aided Verification - 30th International Conference, CAV 2018
+// https://cs.stanford.edu/people/niemetz/publication/2018/niemetzpreinerwolfbiere-cav18/
+
#include "kernel/rtlil.h"
#include "kernel/register.h"
#include "kernel/sigtools.h"
@@ -129,7 +134,13 @@ struct BtorWorker
void export_cell(Cell *cell)
{
- log_assert(cell_recursion_guard.count(cell) == 0);
+ if (cell_recursion_guard.count(cell)) {
+ string cell_list;
+ for (auto c : cell_recursion_guard)
+ cell_list += stringf("\n %s", log_id(c));
+ log_error("Found topological loop while processing cell %s. Active cells:%s\n", log_id(cell), cell_list.c_str());
+ }
+
cell_recursion_guard.insert(cell);
btorf_push(log_id(cell));
@@ -340,7 +351,7 @@ struct BtorWorker
if (cell->type == "$lt") btor_op = "lt";
if (cell->type == "$le") btor_op = "lte";
if (cell->type.in("$eq", "$eqx")) btor_op = "eq";
- if (cell->type.in("$ne", "$nex")) btor_op = "ne";
+ if (cell->type.in("$ne", "$nex")) btor_op = "neq";
if (cell->type == "$ge") btor_op = "gte";
if (cell->type == "$gt") btor_op = "gt";
log_assert(!btor_op.empty());
@@ -869,9 +880,28 @@ struct BtorWorker
else
{
if (bit_cell.count(bit) == 0)
- log_error("No driver for signal bit %s.\n", log_signal(bit));
- export_cell(bit_cell.at(bit));
- log_assert(bit_nid.count(bit));
+ {
+ SigSpec s = bit;
+
+ while (i+GetSize(s) < GetSize(sig) && sig[i+GetSize(s)].wire != nullptr &&
+ bit_cell.count(sig[i+GetSize(s)]) == 0)
+ s.append(sig[i+GetSize(s)]);
+
+ log_warning("No driver for signal %s.\n", log_signal(s));
+
+ int sid = get_bv_sid(GetSize(s));
+ int nid = next_nid++;
+ btorf("%d input %d %s\n", nid, sid);
+ nid_width[nid] = GetSize(s);
+
+ i += GetSize(s)-1;
+ continue;
+ }
+ else
+ {
+ export_cell(bit_cell.at(bit));
+ log_assert(bit_nid.count(bit));
+ }
}
}
diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc
index 7e30b67a..6d946953 100644
--- a/backends/edif/edif.cc
+++ b/backends/edif/edif.cc
@@ -178,7 +178,7 @@ struct EdifBackend : public Backend {
for (auto module_it : design->modules_)
{
RTLIL::Module *module = module_it.second;
- if (module->get_bool_attribute("\\blackbox"))
+ if (module->get_blackbox_attribute())
continue;
if (top_module_name.empty())
@@ -192,7 +192,7 @@ struct EdifBackend : public Backend {
for (auto cell_it : module->cells_)
{
RTLIL::Cell *cell = cell_it.second;
- if (!design->modules_.count(cell->type) || design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) {
+ if (!design->modules_.count(cell->type) || design->modules_.at(cell->type)->get_blackbox_attribute()) {
lib_cell_ports[cell->type];
for (auto p : cell->connections())
lib_cell_ports[cell->type][p.first] = GetSize(p.second);
@@ -302,7 +302,7 @@ struct EdifBackend : public Backend {
*f << stringf(" (technology (numberDefinition))\n");
for (auto module : sorted_modules)
{
- if (module->get_bool_attribute("\\blackbox"))
+ if (module->get_blackbox_attribute())
continue;
SigMap sigmap(module);
diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc
index eef6401b..1c7a7351 100644
--- a/backends/firrtl/firrtl.cc
+++ b/backends/firrtl/firrtl.cc
@@ -106,6 +106,125 @@ struct FirrtlWorker
RTLIL::Design *design;
std::string indent;
+ // Define read/write ports and memories.
+ // We'll collect their definitions and emit the corresponding FIRRTL definitions at the appropriate point in module construction.
+ // For the moment, we don't handle $readmemh or $readmemb.
+ // These will be part of a subsequent PR.
+ struct read_port {
+ string name;
+ bool clk_enable;
+ bool clk_parity;
+ bool transparent;
+ RTLIL::SigSpec clk;
+ RTLIL::SigSpec ena;
+ RTLIL::SigSpec addr;
+ read_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr) : name(name), clk_enable(clk_enable), clk_parity(clk_parity), transparent(transparent), clk(clk), ena(ena), addr(addr) {
+ // Current (3/13/2019) conventions:
+ // generate a constant 0 for clock and a constant 1 for enable if they are undefined.
+ if (!clk.is_fully_def())
+ this->clk = SigSpec(RTLIL::Const(0, 1));
+ if (!ena.is_fully_def())
+ this->ena = SigSpec(RTLIL::Const(1, 1));
+ }
+ string gen_read(const char * indent) {
+ string addr_expr = make_expr(addr);
+ string ena_expr = make_expr(ena);
+ string clk_expr = make_expr(clk);
+ string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str());
+ string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str());
+ string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str());
+ return addr_str + ena_str + clk_str;
+ }
+ };
+ struct write_port : read_port {
+ RTLIL::SigSpec mask;
+ write_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr, RTLIL::SigSpec mask) : read_port(name, clk_enable, clk_parity, transparent, clk, ena, addr), mask(mask) {
+ if (!clk.is_fully_def())
+ this->clk = SigSpec(RTLIL::Const(0));
+ if (!ena.is_fully_def())
+ this->ena = SigSpec(RTLIL::Const(0));
+ if (!mask.is_fully_def())
+ this->ena = SigSpec(RTLIL::Const(1));
+ }
+ string gen_read(const char * /* indent */) {
+ log_error("gen_read called on write_port: %s\n", name.c_str());
+ return stringf("gen_read called on write_port: %s\n", name.c_str());
+ }
+ string gen_write(const char * indent) {
+ string addr_expr = make_expr(addr);
+ string ena_expr = make_expr(ena);
+ string clk_expr = make_expr(clk);
+ string mask_expr = make_expr(mask);
+ string mask_str = stringf("%s%s.mask <= %s\n", indent, name.c_str(), mask_expr.c_str());
+ string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str());
+ string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str());
+ string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str());
+ return addr_str + ena_str + clk_str + mask_str;
+ }
+ };
+ /* Memories defined within this module. */
+ struct memory {
+ Cell *pCell; // for error reporting
+ string name; // memory name
+ int abits; // number of address bits
+ int size; // size (in units) of the memory
+ int width; // size (in bits) of each element
+ int read_latency;
+ int write_latency;
+ vector<read_port> read_ports;
+ vector<write_port> write_ports;
+ std::string init_file;
+ std::string init_file_srcFileSpec;
+ string srcLine;
+ memory(Cell *pCell, string name, int abits, int size, int width) : pCell(pCell), name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") {
+ // Provide defaults for abits or size if one (but not the other) is specified.
+ if (this->abits == 0 && this->size != 0) {
+ this->abits = ceil_log2(this->size);
+ } else if (this->abits != 0 && this->size == 0) {
+ this->size = 1 << this->abits;
+ }
+ // Sanity-check this construction.
+ if (this->name == "") {
+ log_error("Nameless memory%s\n", this->atLine());
+ }
+ if (this->abits == 0 && this->size == 0) {
+ log_error("Memory %s has zero address bits and size%s\n", this->name.c_str(), this->atLine());
+ }
+ if (this->width == 0) {
+ log_error("Memory %s has zero width%s\n", this->name.c_str(), this->atLine());
+ }
+ }
+ // We need a default constructor for the dict insert.
+ memory() : pCell(0), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){}
+
+ const char *atLine() {
+ if (srcLine == "") {
+ if (pCell) {
+ auto p = pCell->attributes.find("\\src");
+ srcLine = " at " + p->second.decode_string();
+ }
+ }
+ return srcLine.c_str();
+ }
+ void add_memory_read_port(read_port &rp) {
+ read_ports.push_back(rp);
+ }
+ void add_memory_write_port(write_port &wp) {
+ write_ports.push_back(wp);
+ }
+ void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) {
+ this->init_file = init_file;
+ this->init_file_srcFileSpec = init_file_srcFileSpec;
+ }
+
+ };
+ dict<string, memory> memories;
+
+ void register_memory(memory &m)
+ {
+ memories[m.name] = m;
+ }
+
void register_reverse_wire_map(string id, SigSpec sig)
{
for (int i = 0; i < GetSize(sig); i++)
@@ -116,7 +235,7 @@ struct FirrtlWorker
{
}
- string make_expr(const SigSpec &sig)
+ static string make_expr(const SigSpec &sig)
{
string expr;
@@ -225,6 +344,7 @@ struct FirrtlWorker
switch (dir) {
case FD_INOUT:
log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", cell_type.c_str(), log_signal(it->second));
+ /* FALLTHRU */
case FD_OUT:
sourceExpr = firstName;
sinkExpr = secondExpr;
@@ -232,7 +352,7 @@ struct FirrtlWorker
break;
case FD_NODIRECTION:
log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", cell_type.c_str(), log_signal(it->second));
- /* FALL_THROUGH */
+ /* FALLTHRU */
case FD_IN:
sourceExpr = secondExpr;
sinkExpr = firstName;
@@ -329,8 +449,10 @@ struct FirrtlWorker
string primop;
bool always_uint = false;
if (cell->type == "$not") primop = "not";
- else if (cell->type == "$neg") primop = "neg";
- else if (cell->type == "$logic_not") {
+ else if (cell->type == "$neg") {
+ primop = "neg";
+ is_signed = true; // Result of "neg" is signed (an SInt).
+ } else if (cell->type == "$logic_not") {
primop = "eq";
a_expr = stringf("%s, UInt(0)", a_expr.c_str());
}
@@ -442,6 +564,7 @@ struct FirrtlWorker
auto b_sig = cell->getPort("\\B");
if (b_sig.is_fully_const()) {
primop = "shl";
+ b_expr = std::to_string(b_sig.as_int());
} else {
primop = "dshl";
// Convert from FIRRTL left shift semantics.
@@ -455,6 +578,7 @@ struct FirrtlWorker
auto b_sig = cell->getPort("\\B");
if (b_sig.is_fully_const()) {
primop = "shr";
+ b_expr = std::to_string(b_sig.as_int());
} else {
primop = "dshr";
}
@@ -515,6 +639,7 @@ struct FirrtlWorker
int abits = cell->parameters.at("\\ABITS").as_int();
int width = cell->parameters.at("\\WIDTH").as_int();
int size = cell->parameters.at("\\SIZE").as_int();
+ memory m(cell, mem_id, abits, size, width);
int rd_ports = cell->parameters.at("\\RD_PORTS").as_int();
int wr_ports = cell->parameters.at("\\WR_PORTS").as_int();
@@ -531,33 +656,24 @@ struct FirrtlWorker
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 addr_sig = cell->getPort("\\RD_ADDR").extract(i*abits, abits);
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);
+ string addr_expr = make_expr(addr_sig);
+ string name(stringf("%s.r%d", m.name.c_str(), i));
+ bool clk_enable = false;
+ bool clk_parity = true;
+ bool transparency = false;
+ SigSpec ena_sig = RTLIL::SigSpec(RTLIL::State::S1, 1);
+ SigSpec clk_sig = RTLIL::SigSpec(RTLIL::State::S0, 1);
+ read_port rp(name, clk_enable, clk_parity, transparency, clk_sig, ena_sig, addr_sig);
+ m.add_memory_read_port(rp);
+ cell_exprs.push_back(rp.gen_read(indent.c_str()));
+ register_reverse_wire_map(stringf("%s.data", name.c_str()), data_sig);
}
for (int i = 0; i < wr_ports; i++)
@@ -568,9 +684,16 @@ struct FirrtlWorker
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));
+ string name(stringf("%s.w%d", m.name.c_str(), i));
+ bool clk_enable = true;
+ bool clk_parity = true;
+ bool transparency = false;
+ SigSpec addr_sig =cell->getPort("\\WR_ADDR").extract(i*abits, abits);
+ string addr_expr = make_expr(addr_sig);
+ SigSpec data_sig =cell->getPort("\\WR_DATA").extract(i*width, width);
+ string data_expr = make_expr(data_sig);
+ SigSpec clk_sig = cell->getPort("\\WR_CLK").extract(i);
+ string clk_expr = make_expr(clk_sig);
SigSpec wen_sig = cell->getPort("\\WR_EN").extract(i*width, width);
string wen_expr = make_expr(wen_sig[0]);
@@ -579,13 +702,57 @@ struct FirrtlWorker
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()));
+ SigSpec mask_sig = RTLIL::SigSpec(RTLIL::State::S1, 1);
+ write_port wp(name, clk_enable, clk_parity, transparency, clk_sig, wen_sig[0], addr_sig, mask_sig);
+ m.add_memory_write_port(wp);
+ cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), name.c_str(), data_expr.c_str()));
+ cell_exprs.push_back(wp.gen_write(indent.c_str()));
}
+ register_memory(m);
+ continue;
+ }
+ if (cell->type.in("$memwr", "$memrd", "$meminit"))
+ {
+ std::string cell_type = fid(cell->type);
+ std::string mem_id = make_id(cell->parameters["\\MEMID"].decode_string());
+ int abits = cell->parameters.at("\\ABITS").as_int();
+ int width = cell->parameters.at("\\WIDTH").as_int();
+ memory *mp = nullptr;
+ if (cell->type == "$meminit" ) {
+ log_error("$meminit (%s.%s.%s) currently unsupported\n", log_id(module), log_id(cell), mem_id.c_str());
+ } else {
+ // It's a $memwr or $memrd. Remember the read/write port parameters for the eventual FIRRTL memory definition.
+ auto addrSig = cell->getPort("\\ADDR");
+ auto dataSig = cell->getPort("\\DATA");
+ auto enableSig = cell->getPort("\\EN");
+ auto clockSig = cell->getPort("\\CLK");
+ Const clk_enable = cell->parameters.at("\\CLK_ENABLE");
+ Const clk_polarity = cell->parameters.at("\\CLK_POLARITY");
+
+ // Do we already have an entry for this memory?
+ if (memories.count(mem_id) == 0) {
+ memory m(cell, mem_id, abits, 0, width);
+ register_memory(m);
+ }
+ mp = &memories.at(mem_id);
+ int portNum = 0;
+ bool transparency = false;
+ string data_expr = make_expr(dataSig);
+ if (cell->type.in("$memwr")) {
+ portNum = (int) mp->write_ports.size();
+ write_port wp(stringf("%s.w%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig, dataSig);
+ mp->add_memory_write_port(wp);
+ cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), wp.name.c_str(), data_expr.c_str()));
+ cell_exprs.push_back(wp.gen_write(indent.c_str()));
+ } else if (cell->type.in("$memrd")) {
+ portNum = (int) mp->read_ports.size();
+ read_port rp(stringf("%s.r%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig);
+ mp->add_memory_read_port(rp);
+ cell_exprs.push_back(rp.gen_read(indent.c_str()));
+ register_reverse_wire_map(stringf("%s.data", rp.name.c_str()), dataSig);
+ }
+ }
continue;
}
@@ -763,6 +930,24 @@ struct FirrtlWorker
f << stringf("\n");
+ // If we have any memory definitions, output them.
+ for (auto kv : memories) {
+ memory &m = kv.second;
+ f << stringf(" mem %s:\n", m.name.c_str());
+ f << stringf(" data-type => UInt<%d>\n", m.width);
+ f << stringf(" depth => %d\n", m.size);
+ for (int i = 0; i < (int) m.read_ports.size(); i += 1) {
+ f << stringf(" reader => r%d\n", i);
+ }
+ for (int i = 0; i < (int) m.write_ports.size(); i += 1) {
+ f << stringf(" writer => w%d\n", i);
+ }
+ f << stringf(" read-latency => %d\n", m.read_latency);
+ f << stringf(" write-latency => %d\n", m.write_latency);
+ f << stringf(" read-under-write => undefined\n");
+ }
+ f << stringf("\n");
+
for (auto str : cell_exprs)
f << str;
diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc
index dc39e5e0..313af7d5 100644
--- a/backends/ilang/ilang_backend.cc
+++ b/backends/ilang/ilang_backend.cc
@@ -160,7 +160,10 @@ void ILANG_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL::
}
f << stringf("%s" "cell %s %s\n", indent.c_str(), cell->type.c_str(), cell->name.c_str());
for (auto &it : cell->parameters) {
- f << stringf("%s parameter%s %s ", indent.c_str(), (it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "", it.first.c_str());
+ f << stringf("%s parameter%s%s %s ", indent.c_str(),
+ (it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "",
+ (it.second.flags & RTLIL::CONST_FLAG_REAL) != 0 ? " real" : "",
+ it.first.c_str());
dump_const(f, it.second);
f << stringf("\n");
}
@@ -201,6 +204,11 @@ void ILANG_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it)
{
+ for (auto ait = (*it)->attributes.begin(); ait != (*it)->attributes.end(); ++ait) {
+ f << stringf("%s attribute %s ", indent.c_str(), ait->first.c_str());
+ dump_const(f, ait->second);
+ f << stringf("\n");
+ }
f << stringf("%s case ", indent.c_str());
for (size_t i = 0; i < (*it)->compare.size(); i++) {
if (i > 0)
@@ -480,6 +488,7 @@ struct DumpPass : public Pass {
std::stringstream buf;
if (!filename.empty()) {
+ rewrite_filename(filename);
std::ofstream *ff = new std::ofstream;
ff->open(filename.c_str(), append ? std::ofstream::app : std::ofstream::trunc);
if (ff->fail()) {
diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc
index 2eb08dbe..b0e3cd25 100644
--- a/backends/intersynth/intersynth.cc
+++ b/backends/intersynth/intersynth.cc
@@ -127,7 +127,7 @@ struct IntersynthBackend : public Backend {
RTLIL::Module *module = module_it.second;
SigMap sigmap(module);
- if (module->get_bool_attribute("\\blackbox"))
+ if (module->get_blackbox_attribute())
continue;
if (module->memories.size() == 0 && module->processes.size() == 0 && module->cells_.size() == 0)
continue;
diff --git a/backends/json/json.cc b/backends/json/json.cc
index f5c68798..dda4dfed 100644
--- a/backends/json/json.cc
+++ b/backends/json/json.cc
@@ -126,6 +126,10 @@ struct JsonWriter
f << stringf("%s\n", first ? "" : ",");
f << stringf(" %s: {\n", get_name(n).c_str());
f << stringf(" \"direction\": \"%s\",\n", w->port_input ? w->port_output ? "inout" : "input" : "output");
+ if (w->start_offset)
+ f << stringf(" \"offset\": %d,\n", w->start_offset);
+ if (w->upto)
+ f << stringf(" \"upto\": 1,\n");
f << stringf(" \"bits\": %s\n", get_bits(w).c_str());
f << stringf(" }");
first = false;
@@ -189,6 +193,10 @@ struct JsonWriter
f << stringf(" %s: {\n", get_name(w->name).c_str());
f << stringf(" \"hide_name\": %s,\n", w->name[0] == '$' ? "1" : "0");
f << stringf(" \"bits\": %s,\n", get_bits(w).c_str());
+ if (w->start_offset)
+ f << stringf(" \"offset\": %d,\n", w->start_offset);
+ if (w->upto)
+ f << stringf(" \"upto\": 1,\n");
f << stringf(" \"attributes\": {");
write_parameters(w->attributes);
f << stringf("\n }\n");
@@ -525,6 +533,7 @@ struct JsonPass : public Pass {
std::stringstream buf;
if (!filename.empty()) {
+ rewrite_filename(filename);
std::ofstream *ff = new std::ofstream;
ff->open(filename.c_str(), std::ofstream::trunc);
if (ff->fail()) {
diff --git a/backends/protobuf/protobuf.cc b/backends/protobuf/protobuf.cc
index 549fc73a..fff110bb 100644
--- a/backends/protobuf/protobuf.cc
+++ b/backends/protobuf/protobuf.cc
@@ -336,6 +336,7 @@ struct ProtobufPass : public Pass {
std::stringstream buf;
if (!filename.empty()) {
+ rewrite_filename(filename);
std::ofstream *ff = new std::ofstream;
ff->open(filename.c_str(), std::ofstream::trunc);
if (ff->fail()) {
diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc
index 688535f3..e318a405 100644
--- a/backends/smt2/smt2.cc
+++ b/backends/smt2/smt2.cc
@@ -1543,7 +1543,7 @@ struct Smt2Backend : public Backend {
for (auto module : sorted_modules)
{
- if (module->get_bool_attribute("\\blackbox") || module->has_memories_warn() || module->has_processes_warn())
+ if (module->get_blackbox_attribute() || module->has_memories_warn() || module->has_processes_warn())
continue;
log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module));
diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py
index ab20a4af..ae7968a1 100644
--- a/backends/smt2/smtio.py
+++ b/backends/smt2/smtio.py
@@ -1023,6 +1023,8 @@ class MkVcd:
assert t >= self.t
if t != self.t:
if self.t == -1:
+ print("$version Generated by Yosys-SMTBMC $end", file=self.f)
+ print("$timescale 1ns $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)
@@ -1041,7 +1043,10 @@ class MkVcd:
scope = scope[:-1]
while uipath[:-1] != scope:
- print("$scope module %s $end" % uipath[len(scope)], file=self.f)
+ scopename = uipath[len(scope)]
+ if scopename.startswith("$"):
+ scopename = "\\" + scopename
+ print("$scope module %s $end" % scopename, file=self.f)
scope.append(uipath[len(scope)])
if path in self.clocks and self.clocks[path][1] == "event":
diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc
index f379c9c4..d75456c1 100644
--- a/backends/smv/smv.cc
+++ b/backends/smv/smv.cc
@@ -739,7 +739,7 @@ struct SmvBackend : public Backend {
pool<Module*> modules;
for (auto module : design->modules())
- if (!module->get_bool_attribute("\\blackbox") && !module->has_memories_warn() && !module->has_processes_warn())
+ if (!module->get_blackbox_attribute() && !module->has_memories_warn() && !module->has_processes_warn())
modules.insert(module);
if (template_f.is_open())
diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc
index b6a3f1e7..6738a4bb 100644
--- a/backends/spice/spice.cc
+++ b/backends/spice/spice.cc
@@ -212,7 +212,7 @@ struct SpiceBackend : public Backend {
for (auto module_it : design->modules_)
{
RTLIL::Module *module = module_it.second;
- if (module->get_bool_attribute("\\blackbox"))
+ if (module->get_blackbox_attribute())
continue;
if (module->processes.size() != 0)
diff --git a/backends/table/table.cc b/backends/table/table.cc
index b75169ea..796f1805 100644
--- a/backends/table/table.cc
+++ b/backends/table/table.cc
@@ -67,7 +67,7 @@ struct TableBackend : public Backend {
for (auto module : design->modules())
{
- if (module->get_bool_attribute("\\blackbox"))
+ if (module->get_blackbox_attribute())
continue;
SigMap sigmap(module);
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc
index 83d83f48..087c6fec 100644
--- a/backends/verilog/verilog_backend.cc
+++ b/backends/verilog/verilog_backend.cc
@@ -183,10 +183,15 @@ bool is_reg_wire(RTLIL::SigSpec sig, std::string &reg_name)
return true;
}
-void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool set_signed = false, bool escape_comment = false)
+void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool escape_comment = false)
{
+ bool set_signed = (data.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
if (width < 0)
width = data.bits.size() - offset;
+ if (width == 0) {
+ f << "\"\"";
+ return;
+ }
if (nostr)
goto dump_hex;
if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
@@ -271,7 +276,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
}
}
} else {
- f << stringf("\"");
+ if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0)
+ f << stringf("\"");
std::string str = data.decode_string();
for (size_t i = 0; i < str.size(); i++) {
if (str[i] == '\n')
@@ -289,7 +295,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
else
f << str[i];
}
- f << stringf("\"");
+ if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0)
+ f << stringf("\"");
}
}
@@ -340,6 +347,10 @@ void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decima
void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig)
{
+ if (GetSize(sig) == 0) {
+ f << "\"\"";
+ return;
+ }
if (sig.is_chunk()) {
dump_sigchunk(f, sig.as_chunk());
} else {
@@ -353,20 +364,22 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig)
}
}
-void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false)
+void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false, bool as_comment = false)
{
if (noattr)
return;
+ if (attr2comment)
+ as_comment = true;
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
- f << stringf("%s" "%s %s", indent.c_str(), attr2comment ? "/*" : "(*", id(it->first).c_str());
+ f << stringf("%s" "%s %s", indent.c_str(), as_comment ? "/*" : "(*", id(it->first).c_str());
f << stringf(" = ");
if (modattr && (it->second == Const(0, 1) || it->second == Const(0)))
f << stringf(" 0 ");
else if (modattr && (it->second == Const(1, 1) || it->second == Const(1)))
f << stringf(" 1 ");
else
- dump_const(f, it->second, -1, 0, false, false, attr2comment);
- f << stringf(" %s%c", attr2comment ? "*/" : "*)", term);
+ dump_const(f, it->second, -1, 0, false, as_comment);
+ f << stringf(" %s%c", as_comment ? "*/" : "*)", term);
}
}
@@ -1234,6 +1247,118 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
+ if (cell->type.in("$assert", "$assume", "$cover"))
+ {
+ f << stringf("%s" "always @* if (", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\EN"));
+ f << stringf(") %s(", cell->type.c_str()+1);
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf(");\n");
+ return true;
+ }
+
+ if (cell->type.in("$specify2", "$specify3"))
+ {
+ f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str());
+
+ SigSpec en = cell->getPort("\\EN");
+ if (en != State::S1) {
+ f << stringf("if (");
+ dump_sigspec(f, cell->getPort("\\EN"));
+ f << stringf(") ");
+ }
+
+ f << "(";
+ if (cell->type == "$specify3" && cell->getParam("\\EDGE_EN").as_bool())
+ f << (cell->getParam("\\EDGE_POL").as_bool() ? "posedge ": "negedge ");
+
+ dump_sigspec(f, cell->getPort("\\SRC"));
+
+ f << " ";
+ if (cell->getParam("\\SRC_DST_PEN").as_bool())
+ f << (cell->getParam("\\SRC_DST_POL").as_bool() ? "+": "-");
+ f << (cell->getParam("\\FULL").as_bool() ? "*> ": "=> ");
+
+ if (cell->type == "$specify3") {
+ f << "(";
+ dump_sigspec(f, cell->getPort("\\DST"));
+ f << " ";
+ if (cell->getParam("\\DAT_DST_PEN").as_bool())
+ f << (cell->getParam("\\DAT_DST_POL").as_bool() ? "+": "-");
+ f << ": ";
+ dump_sigspec(f, cell->getPort("\\DAT"));
+ f << ")";
+ } else {
+ dump_sigspec(f, cell->getPort("\\DST"));
+ }
+
+ bool bak_decimal = decimal;
+ decimal = 1;
+
+ f << ") = (";
+ dump_const(f, cell->getParam("\\T_RISE_MIN"));
+ f << ":";
+ dump_const(f, cell->getParam("\\T_RISE_TYP"));
+ f << ":";
+ dump_const(f, cell->getParam("\\T_RISE_MAX"));
+ f << ", ";
+ dump_const(f, cell->getParam("\\T_FALL_MIN"));
+ f << ":";
+ dump_const(f, cell->getParam("\\T_FALL_TYP"));
+ f << ":";
+ dump_const(f, cell->getParam("\\T_FALL_MAX"));
+ f << ");\n";
+
+ decimal = bak_decimal;
+
+ f << stringf("%s" "endspecify\n", indent.c_str());
+ return true;
+ }
+
+ if (cell->type == "$specrule")
+ {
+ f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str());
+
+ string spec_type = cell->getParam("\\TYPE").decode_string();
+ f << stringf("%s(", spec_type.c_str());
+
+ if (cell->getParam("\\SRC_PEN").as_bool())
+ f << (cell->getParam("\\SRC_POL").as_bool() ? "posedge ": "negedge ");
+ dump_sigspec(f, cell->getPort("\\SRC"));
+
+ if (cell->getPort("\\SRC_EN") != State::S1) {
+ f << " &&& ";
+ dump_sigspec(f, cell->getPort("\\SRC_EN"));
+ }
+
+ f << ", ";
+ if (cell->getParam("\\DST_PEN").as_bool())
+ f << (cell->getParam("\\DST_POL").as_bool() ? "posedge ": "negedge ");
+ dump_sigspec(f, cell->getPort("\\DST"));
+
+ if (cell->getPort("\\DST_EN") != State::S1) {
+ f << " &&& ";
+ dump_sigspec(f, cell->getPort("\\DST_EN"));
+ }
+
+ bool bak_decimal = decimal;
+ decimal = 1;
+
+ f << ", ";
+ dump_const(f, cell->getParam("\\T_LIMIT"));
+
+ if (spec_type == "$setuphold" || spec_type == "$recrem" || spec_type == "$fullskew") {
+ f << ", ";
+ dump_const(f, cell->getParam("\\T_LIMIT2"));
+ }
+
+ f << ");\n";
+ decimal = bak_decimal;
+
+ f << stringf("%s" "endspecify\n", indent.c_str());
+ return true;
+ }
+
// FIXME: $_SR_[PN][PN]_, $_DLATCH_[PN]_, $_DLATCHSR_[PN][PN][PN]_
// FIXME: $sr, $dlatch, $memrd, $memwr, $fsm
@@ -1256,8 +1381,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
if (it != cell->parameters.begin())
f << stringf(",");
f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str());
- bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
- dump_const(f, it->second, -1, 0, false, is_signed);
+ dump_const(f, it->second);
f << stringf(")");
}
f << stringf("\n%s" ")", indent.c_str());
@@ -1304,8 +1428,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
if (defparam && cell->parameters.size() > 0) {
for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) {
f << stringf("%sdefparam %s.%s = ", indent.c_str(), cell_name.c_str(), id(it->first).c_str());
- bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
- dump_const(f, it->second, -1, 0, false, is_signed);
+ dump_const(f, it->second);
f << stringf(";\n");
}
}
@@ -1371,12 +1494,14 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw
return;
}
+ dump_attributes(f, indent, sw->attributes);
f << stringf("%s" "casez (", indent.c_str());
dump_sigspec(f, sw->signal);
f << stringf(")\n");
bool got_default = false;
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
+ dump_attributes(f, indent + " ", (*it)->attributes, '\n', /*modattr=*/false, /*as_comment=*/true);
if ((*it)->compare.size() == 0) {
if (got_default)
continue;
@@ -1497,7 +1622,8 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
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 (val[i] == State::S0 || val[i] == State::S1)
+ active_initdata[sig[i]] = val[i];
}
if (!module->processes.empty())
@@ -1540,7 +1666,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
}
}
- dump_attributes(f, indent, module->attributes, '\n', true);
+ dump_attributes(f, indent, module->attributes, '\n', /*attr2comment=*/true);
f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str());
bool keep_running = true;
for (int port_id = 1; keep_running; port_id++) {
@@ -1770,7 +1896,7 @@ struct VerilogBackend : public Backend {
*f << stringf("/* Generated by %s */\n", yosys_version_str);
for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) {
- if (it->second->get_bool_attribute("\\blackbox") != blackboxes)
+ if (it->second->get_blackbox_attribute() != blackboxes)
continue;
if (selected && !design->selected_whole_module(it->first)) {
if (design->selected_module(it->first))
diff --git a/examples/python-api/.gitignore b/examples/python-api/.gitignore
new file mode 100644
index 00000000..758de113
--- /dev/null
+++ b/examples/python-api/.gitignore
@@ -0,0 +1 @@
+out/**
diff --git a/examples/python-api/pass.py b/examples/python-api/pass.py
new file mode 100755
index 00000000..d67cf4a2
--- /dev/null
+++ b/examples/python-api/pass.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python3
+
+from pyosys import libyosys as ys
+
+import matplotlib.pyplot as plt
+import numpy as np
+
+class CellStatsPass(ys.Pass):
+
+ def __init__(self):
+ super().__init__("cell_stats", "Shows cell stats as plot")
+
+ def py_help(self):
+ ys.log("This pass uses the matplotlib library to display cell stats\n")
+
+ def py_execute(self, args, design):
+ ys.log_header(design, "Plotting cell stats\n")
+ cell_stats = {}
+ for module in design.selected_whole_modules_warn():
+ for cell in module.selected_cells():
+ if cell.type.str() in cell_stats:
+ cell_stats[cell.type.str()] += 1
+ else:
+ cell_stats[cell.type.str()] = 1
+ plt.bar(range(len(cell_stats)), height = list(cell_stats.values()),align='center')
+ plt.xticks(range(len(cell_stats)), list(cell_stats.keys()))
+ plt.show()
+
+ def py_clear_flags(self):
+ ys.log("Clear Flags - CellStatsPass\n")
+
+p = CellStatsPass()
diff --git a/examples/python-api/script.py b/examples/python-api/script.py
new file mode 100755
index 00000000..f0fa5a0b
--- /dev/null
+++ b/examples/python-api/script.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python3
+
+from pyosys import libyosys as ys
+
+import matplotlib.pyplot as plt
+import numpy as np
+
+design = ys.Design()
+ys.run_pass("read_verilog ../../tests/simple/fiedler-cooley.v", design);
+ys.run_pass("prep", design)
+ys.run_pass("opt -full", design)
+
+cell_stats = {}
+for module in design.selected_whole_modules_warn():
+ for cell in module.selected_cells():
+ if cell.type.str() in cell_stats:
+ cell_stats[cell.type.str()] += 1
+ else:
+ cell_stats[cell.type.str()] = 1
+plt.bar(range(len(cell_stats)), height = list(cell_stats.values()),align='center')
+plt.xticks(range(len(cell_stats)), list(cell_stats.keys()))
+plt.show()
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc
index cf7950c8..68552fd0 100644
--- a/frontends/aiger/aigerparse.cc
+++ b/frontends/aiger/aigerparse.cc
@@ -33,353 +33,390 @@
YOSYS_NAMESPACE_BEGIN
-#define log_debug log
-
AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name)
- : design(design), f(f), clk_name(clk_name)
+ : design(design), f(f), clk_name(clk_name)
{
- module = new RTLIL::Module;
- module->name = module_name;
- if (design->module(module->name))
- log_error("Duplicate definition of module %s!\n", log_id(module->name));
+ module = new RTLIL::Module;
+ module->name = module_name;
+ if (design->module(module->name))
+ log_error("Duplicate definition of module %s!\n", log_id(module->name));
}
void AigerReader::parse_aiger()
{
- std::string header;
- f >> header;
- if (header != "aag" && header != "aig")
- log_error("Unsupported AIGER file!\n");
-
- // Parse rest of header
- if (!(f >> M >> I >> L >> O >> A))
- log_error("Invalid AIGER header\n");
-
- // Optional values
- B = C = J = F = 0;
- for (auto &i : std::array<std::reference_wrapper<unsigned>,4>{B, C, J, F}) {
- if (f.peek() != ' ') break;
- if (!(f >> i))
- log_error("Invalid AIGER header\n");
- }
-
- std::string line;
- std::getline(f, line); // Ignore up to start of next line, as standard
- // says anything that follows could be used for
- // optional sections
-
- log_debug("M=%u I=%u L=%u O=%u A=%u B=%u C=%u J=%u F=%u\n", M, I, L, O, A, B, C, J, F);
-
- line_count = 1;
-
- if (header == "aag")
- parse_aiger_ascii();
- else if (header == "aig")
- parse_aiger_binary();
- else
- log_abort();
-
- // Parse footer (symbol table, comments, etc.)
- unsigned l1;
- std::string s;
- for (int c = f.peek(); c != EOF; c = f.peek(), ++line_count) {
- if (c == 'i' || c == 'l' || c == 'o') {
- f.ignore(1);
- if (!(f >> l1 >> s))
- log_error("Line %u cannot be interpreted as a symbol entry!\n", line_count);
-
- if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size()))
- log_error("Line %u has invalid symbol position!\n", line_count);
-
- RTLIL::Wire* wire;
- if (c == 'i') wire = inputs[l1];
- else if (c == 'l') wire = latches[l1];
- else if (c == 'o') wire = outputs[l1];
- else log_abort();
-
- module->rename(wire, stringf("\\%s", s.c_str()));
- }
- else if (c == 'b' || c == 'j' || c == 'f') {
- // TODO
- }
- else if (c == 'c') {
- f.ignore(1);
- if (f.peek() == '\n')
- break;
- // Else constraint (TODO)
- }
- else
- log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c);
- std::getline(f, line); // Ignore up to start of next line
- }
-
- module->fixup_ports();
- design->add(module);
+ std::string header;
+ f >> header;
+ if (header != "aag" && header != "aig")
+ log_error("Unsupported AIGER file!\n");
+
+ // Parse rest of header
+ if (!(f >> M >> I >> L >> O >> A))
+ log_error("Invalid AIGER header\n");
+
+ // Optional values
+ B = C = J = F = 0;
+ if (f.peek() != ' ') goto end_of_header;
+ if (!(f >> B)) log_error("Invalid AIGER header\n");
+ if (f.peek() != ' ') goto end_of_header;
+ if (!(f >> C)) log_error("Invalid AIGER header\n");
+ if (f.peek() != ' ') goto end_of_header;
+ if (!(f >> J)) log_error("Invalid AIGER header\n");
+ if (f.peek() != ' ') goto end_of_header;
+ if (!(f >> F)) log_error("Invalid AIGER header\n");
+end_of_header:
+
+ std::string line;
+ std::getline(f, line); // Ignore up to start of next line, as standard
+ // says anything that follows could be used for
+ // optional sections
+
+ log_debug("M=%u I=%u L=%u O=%u A=%u B=%u C=%u J=%u F=%u\n", M, I, L, O, A, B, C, J, F);
+
+ line_count = 1;
+
+ if (header == "aag")
+ parse_aiger_ascii();
+ else if (header == "aig")
+ parse_aiger_binary();
+ else
+ log_abort();
+
+ RTLIL::Wire* n0 = module->wire("\\n0");
+ if (n0)
+ module->connect(n0, RTLIL::S0);
+
+ for (unsigned i = 0; i < outputs.size(); ++i) {
+ RTLIL::Wire *wire = outputs[i];
+ if (wire->port_input) {
+ RTLIL::Wire *o_wire = module->addWire(wire->name.str() + "_o");
+ o_wire->port_output = true;
+ wire->port_output = false;
+ module->connect(o_wire, wire);
+ outputs[i] = o_wire;
+ }
+ }
+
+ // Parse footer (symbol table, comments, etc.)
+ unsigned l1;
+ std::string s;
+ for (int c = f.peek(); c != EOF; c = f.peek(), ++line_count) {
+ if (c == 'i' || c == 'l' || c == 'o' || c == 'b') {
+ f.ignore(1);
+ if (!(f >> l1 >> s))
+ log_error("Line %u cannot be interpreted as a symbol entry!\n", line_count);
+
+ if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size()))
+ log_error("Line %u has invalid symbol position!\n", line_count);
+
+ RTLIL::Wire* wire;
+ if (c == 'i') wire = inputs[l1];
+ else if (c == 'l') wire = latches[l1];
+ else if (c == 'o') wire = outputs[l1];
+ else if (c == 'b') wire = bad_properties[l1];
+ else log_abort();
+
+ module->rename(wire, stringf("\\%s", s.c_str()));
+ }
+ else if (c == 'j' || c == 'f') {
+ // TODO
+ }
+ else if (c == 'c') {
+ f.ignore(1);
+ if (f.peek() == '\n')
+ break;
+ // Else constraint (TODO)
+ }
+ else
+ log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c);
+ std::getline(f, line); // Ignore up to start of next line
+ }
+
+ module->fixup_ports();
+ design->add(module);
}
static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal)
{
- const unsigned variable = literal >> 1;
- const bool invert = literal & 1;
- RTLIL::IdString wire_name(stringf("\\n%d%s", variable, invert ? "_inv" : "")); // FIXME: is "_inv" the right suffix?
- RTLIL::Wire *wire = module->wire(wire_name);
- if (wire) return wire;
- log_debug("Creating %s\n", wire_name.c_str());
- wire = module->addWire(wire_name);
- if (!invert) return wire;
- RTLIL::IdString wire_inv_name(stringf("\\n%d", variable));
- RTLIL::Wire *wire_inv = module->wire(wire_inv_name);
- if (wire_inv) {
- if (module->cell(wire_inv_name)) return wire;
- }
- else {
- log_debug("Creating %s\n", wire_inv_name.c_str());
- wire_inv = module->addWire(wire_inv_name);
- }
-
- log_debug("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str());
- module->addNotGate(stringf("\\n%d_not", variable), wire_inv, wire); // FIXME: is "_not" the right suffix?
-
- return wire;
+ const unsigned variable = literal >> 1;
+ const bool invert = literal & 1;
+ RTLIL::IdString wire_name(stringf("\\n%d%s", variable, invert ? "_inv" : "")); // FIXME: is "_inv" the right suffix?
+ RTLIL::Wire *wire = module->wire(wire_name);
+ if (wire) return wire;
+ log_debug("Creating %s\n", wire_name.c_str());
+ wire = module->addWire(wire_name);
+ if (!invert) return wire;
+ RTLIL::IdString wire_inv_name(stringf("\\n%d", variable));
+ RTLIL::Wire *wire_inv = module->wire(wire_inv_name);
+ if (wire_inv) {
+ if (module->cell(wire_inv_name)) return wire;
+ }
+ else {
+ log_debug("Creating %s\n", wire_inv_name.c_str());
+ wire_inv = module->addWire(wire_inv_name);
+ }
+
+ log_debug("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str());
+ module->addNotGate(stringf("\\n%d_not", variable), wire_inv, wire); // FIXME: is "_not" the right suffix?
+
+ return wire;
}
void AigerReader::parse_aiger_ascii()
{
- std::string line;
- std::stringstream ss;
-
- unsigned l1, l2, l3;
-
- // Parse inputs
- for (unsigned i = 0; i < I; ++i, ++line_count) {
- if (!(f >> l1))
- log_error("Line %u cannot be interpreted as an input!\n", line_count);
- log_debug("%d is an input\n", l1);
- log_assert(!(l1 & 1)); // TODO: Inputs can't be inverted?
- RTLIL::Wire *wire = createWireIfNotExists(module, l1);
- wire->port_input = true;
- inputs.push_back(wire);
- }
-
- // Parse latches
- RTLIL::Wire *clk_wire = nullptr;
- if (L > 0) {
- clk_wire = module->wire(clk_name);
- log_assert(!clk_wire);
- log_debug("Creating %s\n", clk_name.c_str());
- clk_wire = module->addWire(clk_name);
- clk_wire->port_input = true;
- }
- for (unsigned i = 0; i < L; ++i, ++line_count) {
- if (!(f >> l1 >> l2))
- log_error("Line %u cannot be interpreted as a latch!\n", line_count);
- log_debug("%d %d is a latch\n", l1, l2);
- log_assert(!(l1 & 1)); // TODO: Latch outputs can't be inverted?
- RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
- RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
-
- module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire);
-
- // Reset logic is optional in AIGER 1.9
- if (f.peek() == ' ') {
- if (!(f >> l3))
- log_error("Line %u cannot be interpreted as a latch!\n", line_count);
-
- if (l3 == 0 || l3 == 1)
- q_wire->attributes["\\init"] = RTLIL::Const(l3);
- else if (l3 == l1) {
- //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx);
- }
- else
- log_error("Line %u has invalid reset literal for latch!\n", line_count);
- }
- else {
- // AIGER latches are assumed to be initialized to zero
- q_wire->attributes["\\init"] = RTLIL::Const(0);
- }
- latches.push_back(q_wire);
- }
-
- // Parse outputs
- for (unsigned i = 0; i < O; ++i, ++line_count) {
- if (!(f >> l1))
- log_error("Line %u cannot be interpreted as an output!\n", line_count);
-
- log_debug("%d is an output\n", l1);
- RTLIL::Wire *wire = createWireIfNotExists(module, l1);
- wire->port_output = true;
- outputs.push_back(wire);
- }
- std::getline(f, line); // Ignore up to start of next line
-
- // TODO: Parse bad state properties
- for (unsigned i = 0; i < B; ++i, ++line_count)
- std::getline(f, line); // Ignore up to start of next line
-
- // TODO: Parse invariant constraints
- for (unsigned i = 0; i < C; ++i, ++line_count)
- std::getline(f, line); // Ignore up to start of next line
-
- // TODO: Parse justice properties
- for (unsigned i = 0; i < J; ++i, ++line_count)
- std::getline(f, line); // Ignore up to start of next line
-
- // TODO: Parse fairness constraints
- for (unsigned i = 0; i < F; ++i, ++line_count)
- std::getline(f, line); // Ignore up to start of next line
-
- // Parse AND
- for (unsigned i = 0; i < A; ++i) {
- if (!(f >> l1 >> l2 >> l3))
- log_error("Line %u cannot be interpreted as an AND!\n", line_count);
-
- log_debug("%d %d %d is an AND\n", l1, l2, l3);
- log_assert(!(l1 & 1)); // TODO: Output of ANDs can't be inverted?
- RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
- RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
- RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
- module->addAndGate(NEW_ID, i1_wire, i2_wire, o_wire);
- }
- std::getline(f, line); // Ignore up to start of next line
+ std::string line;
+ std::stringstream ss;
+
+ unsigned l1, l2, l3;
+
+ // Parse inputs
+ for (unsigned i = 1; i <= I; ++i, ++line_count) {
+ if (!(f >> l1))
+ log_error("Line %u cannot be interpreted as an input!\n", line_count);
+ log_debug("%d is an input\n", l1);
+ log_assert(!(l1 & 1)); // TODO: Inputs can't be inverted?
+ RTLIL::Wire *wire = createWireIfNotExists(module, l1);
+ wire->port_input = true;
+ inputs.push_back(wire);
+ }
+
+ // Parse latches
+ RTLIL::Wire *clk_wire = nullptr;
+ if (L > 0) {
+ clk_wire = module->wire(clk_name);
+ log_assert(!clk_wire);
+ log_debug("Creating %s\n", clk_name.c_str());
+ clk_wire = module->addWire(clk_name);
+ clk_wire->port_input = true;
+ }
+ for (unsigned i = 0; i < L; ++i, ++line_count) {
+ if (!(f >> l1 >> l2))
+ log_error("Line %u cannot be interpreted as a latch!\n", line_count);
+ log_debug("%d %d is a latch\n", l1, l2);
+ log_assert(!(l1 & 1)); // TODO: Latch outputs can't be inverted?
+ RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
+ RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
+
+ module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire);
+
+ // Reset logic is optional in AIGER 1.9
+ if (f.peek() == ' ') {
+ if (!(f >> l3))
+ log_error("Line %u cannot be interpreted as a latch!\n", line_count);
+
+ if (l3 == 0)
+ q_wire->attributes["\\init"] = RTLIL::S0;
+ else if (l3 == 1)
+ q_wire->attributes["\\init"] = RTLIL::S1;
+ else if (l3 == l1) {
+ //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx);
+ }
+ else
+ log_error("Line %u has invalid reset literal for latch!\n", line_count);
+ }
+ else {
+ // AIGER latches are assumed to be initialized to zero
+ q_wire->attributes["\\init"] = RTLIL::S0;
+ }
+ latches.push_back(q_wire);
+ }
+
+ // Parse outputs
+ for (unsigned i = 0; i < O; ++i, ++line_count) {
+ if (!(f >> l1))
+ log_error("Line %u cannot be interpreted as an output!\n", line_count);
+
+ log_debug("%d is an output\n", l1);
+ RTLIL::Wire *wire = createWireIfNotExists(module, l1);
+ wire->port_output = true;
+ outputs.push_back(wire);
+ }
+
+ // Parse bad properties
+ for (unsigned i = 0; i < B; ++i, ++line_count) {
+ if (!(f >> l1))
+ log_error("Line %u cannot be interpreted as a bad state property!\n", line_count);
+
+ log_debug("%d is a bad state property\n", l1);
+ RTLIL::Wire *wire = createWireIfNotExists(module, l1);
+ wire->port_output = true;
+ bad_properties.push_back(wire);
+ }
+
+ // TODO: Parse invariant constraints
+ for (unsigned i = 0; i < C; ++i, ++line_count)
+ std::getline(f, line); // Ignore up to start of next line
+
+ // TODO: Parse justice properties
+ for (unsigned i = 0; i < J; ++i, ++line_count)
+ std::getline(f, line); // Ignore up to start of next line
+
+ // TODO: Parse fairness constraints
+ for (unsigned i = 0; i < F; ++i, ++line_count)
+ std::getline(f, line); // Ignore up to start of next line
+
+ // Parse AND
+ for (unsigned i = 0; i < A; ++i) {
+ if (!(f >> l1 >> l2 >> l3))
+ log_error("Line %u cannot be interpreted as an AND!\n", line_count);
+
+ log_debug("%d %d %d is an AND\n", l1, l2, l3);
+ log_assert(!(l1 & 1)); // TODO: Output of ANDs can't be inverted?
+ RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
+ RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
+ RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
+ module->addAndGate(NEW_ID, i1_wire, i2_wire, o_wire);
+ }
+ std::getline(f, line); // Ignore up to start of next line
}
static unsigned parse_next_delta_literal(std::istream &f, unsigned ref)
{
- unsigned x = 0, i = 0;
- unsigned char ch;
- while ((ch = f.get()) & 0x80)
- x |= (ch & 0x7f) << (7 * i++);
- return ref - (x | (ch << (7 * i)));
+ unsigned x = 0, i = 0;
+ unsigned char ch;
+ while ((ch = f.get()) & 0x80)
+ x |= (ch & 0x7f) << (7 * i++);
+ return ref - (x | (ch << (7 * i)));
}
void AigerReader::parse_aiger_binary()
{
- unsigned l1, l2, l3;
- std::string line;
-
- // Parse inputs
- for (unsigned i = 1; i <= I; ++i) {
- RTLIL::Wire *wire = createWireIfNotExists(module, i << 1);
- wire->port_input = true;
- inputs.push_back(wire);
- }
-
- // Parse latches
- RTLIL::Wire *clk_wire = nullptr;
- if (L > 0) {
- clk_wire = module->wire(clk_name);
- log_assert(!clk_wire);
- log_debug("Creating %s\n", clk_name.c_str());
- clk_wire = module->addWire(clk_name);
- clk_wire->port_input = true;
- }
- l1 = (I+1) * 2;
- for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) {
- if (!(f >> l2))
- log_error("Line %u cannot be interpreted as a latch!\n", line_count);
- log_debug("%d %d is a latch\n", l1, l2);
- RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
- RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
-
- module->addDff(NEW_ID, clk_wire, d_wire, q_wire);
-
- // Reset logic is optional in AIGER 1.9
- if (f.peek() == ' ') {
- if (!(f >> l3))
- log_error("Line %u cannot be interpreted as a latch!\n", line_count);
-
- if (l3 == 0 || l3 == 1)
- q_wire->attributes["\\init"] = RTLIL::Const(l3);
- else if (l3 == l1) {
- //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx);
- }
- else
- log_error("Line %u has invalid reset literal for latch!\n", line_count);
- }
- else {
- // AIGER latches are assumed to be initialized to zero
- q_wire->attributes["\\init"] = RTLIL::Const(0);
- }
- latches.push_back(q_wire);
- }
-
- // Parse outputs
- for (unsigned i = 0; i < O; ++i, ++line_count) {
- if (!(f >> l1))
- log_error("Line %u cannot be interpreted as an output!\n", line_count);
-
- log_debug("%d is an output\n", l1);
- RTLIL::Wire *wire = createWireIfNotExists(module, l1);
- wire->port_output = true;
- outputs.push_back(wire);
- }
- std::getline(f, line); // Ignore up to start of next line
-
- // TODO: Parse bad state properties
- for (unsigned i = 0; i < B; ++i, ++line_count)
- std::getline(f, line); // Ignore up to start of next line
-
- // TODO: Parse invariant constraints
- for (unsigned i = 0; i < C; ++i, ++line_count)
- std::getline(f, line); // Ignore up to start of next line
-
- // TODO: Parse justice properties
- for (unsigned i = 0; i < J; ++i, ++line_count)
- std::getline(f, line); // Ignore up to start of next line
-
- // TODO: Parse fairness constraints
- for (unsigned i = 0; i < F; ++i, ++line_count)
- std::getline(f, line); // Ignore up to start of next line
-
- // Parse AND
- l1 = (I+L+1) << 1;
- for (unsigned i = 0; i < A; ++i, ++line_count, l1 += 2) {
- l2 = parse_next_delta_literal(f, l1);
- l3 = parse_next_delta_literal(f, l2);
-
- log_debug("%d %d %d is an AND\n", l1, l2, l3);
- log_assert(!(l1 & 1)); // TODO: Output of ANDs can't be inverted?
- RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
- RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
- RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
-
- RTLIL::Cell *and_cell = module->addCell(NEW_ID, "$_AND_");
- and_cell->setPort("\\A", i1_wire);
- and_cell->setPort("\\B", i2_wire);
- and_cell->setPort("\\Y", o_wire);
- }
+ unsigned l1, l2, l3;
+ std::string line;
+
+ // Parse inputs
+ for (unsigned i = 1; i <= I; ++i) {
+ RTLIL::Wire *wire = createWireIfNotExists(module, i << 1);
+ wire->port_input = true;
+ inputs.push_back(wire);
+ }
+
+ // Parse latches
+ RTLIL::Wire *clk_wire = nullptr;
+ if (L > 0) {
+ clk_wire = module->wire(clk_name);
+ log_assert(!clk_wire);
+ log_debug("Creating %s\n", clk_name.c_str());
+ clk_wire = module->addWire(clk_name);
+ clk_wire->port_input = true;
+ }
+ l1 = (I+1) * 2;
+ for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) {
+ if (!(f >> l2))
+ log_error("Line %u cannot be interpreted as a latch!\n", line_count);
+ log_debug("%d %d is a latch\n", l1, l2);
+ RTLIL::Wire *q_wire = createWireIfNotExists(module, l1);
+ RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
+
+ module->addDff(NEW_ID, clk_wire, d_wire, q_wire);
+
+ // Reset logic is optional in AIGER 1.9
+ if (f.peek() == ' ') {
+ if (!(f >> l3))
+ log_error("Line %u cannot be interpreted as a latch!\n", line_count);
+
+ if (l3 == 0)
+ q_wire->attributes["\\init"] = RTLIL::S0;
+ else if (l3 == 1)
+ q_wire->attributes["\\init"] = RTLIL::S1;
+ else if (l3 == l1) {
+ //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx);
+ }
+ else
+ log_error("Line %u has invalid reset literal for latch!\n", line_count);
+ }
+ else {
+ // AIGER latches are assumed to be initialized to zero
+ q_wire->attributes["\\init"] = RTLIL::S0;
+ }
+ latches.push_back(q_wire);
+ }
+
+ // Parse outputs
+ for (unsigned i = 0; i < O; ++i, ++line_count) {
+ if (!(f >> l1))
+ log_error("Line %u cannot be interpreted as an output!\n", line_count);
+
+ log_debug("%d is an output\n", l1);
+ RTLIL::Wire *wire = createWireIfNotExists(module, l1);
+ wire->port_output = true;
+ outputs.push_back(wire);
+ }
+ std::getline(f, line); // Ignore up to start of next line
+
+ // Parse bad properties
+ for (unsigned i = 0; i < B; ++i, ++line_count) {
+ if (!(f >> l1))
+ log_error("Line %u cannot be interpreted as a bad state property!\n", line_count);
+
+ log_debug("%d is a bad state property\n", l1);
+ RTLIL::Wire *wire = createWireIfNotExists(module, l1);
+ wire->port_output = true;
+ bad_properties.push_back(wire);
+ }
+ if (B > 0)
+ std::getline(f, line); // Ignore up to start of next line
+
+ // TODO: Parse invariant constraints
+ for (unsigned i = 0; i < C; ++i, ++line_count)
+ std::getline(f, line); // Ignore up to start of next line
+
+ // TODO: Parse justice properties
+ for (unsigned i = 0; i < J; ++i, ++line_count)
+ std::getline(f, line); // Ignore up to start of next line
+
+ // TODO: Parse fairness constraints
+ for (unsigned i = 0; i < F; ++i, ++line_count)
+ std::getline(f, line); // Ignore up to start of next line
+
+ // Parse AND
+ l1 = (I+L+1) << 1;
+ for (unsigned i = 0; i < A; ++i, ++line_count, l1 += 2) {
+ l2 = parse_next_delta_literal(f, l1);
+ l3 = parse_next_delta_literal(f, l2);
+
+ log_debug("%d %d %d is an AND\n", l1, l2, l3);
+ log_assert(!(l1 & 1)); // TODO: Output of ANDs can't be inverted?
+ RTLIL::Wire *o_wire = createWireIfNotExists(module, l1);
+ RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2);
+ RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3);
+
+ RTLIL::Cell *and_cell = module->addCell(NEW_ID, "$_AND_");
+ and_cell->setPort("\\A", i1_wire);
+ and_cell->setPort("\\B", i2_wire);
+ and_cell->setPort("\\Y", o_wire);
+ }
}
struct AigerFrontend : public Frontend {
- AigerFrontend() : Frontend("aiger", "read AIGER file") { }
- void help() YS_OVERRIDE
- {
- // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
- log("\n");
- log(" read_aiger [options] [filename]\n");
- log("\n");
- log("Load module from an AIGER file into the current design.\n");
- log("\n");
- log(" -module_name <module_name>\n");
- log(" Name of module to be created (default: <filename>)"
+ AigerFrontend() : Frontend("aiger", "read AIGER file") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" read_aiger [options] [filename]\n");
+ log("\n");
+ log("Load module from an AIGER file into the current design.\n");
+ log("\n");
+ log(" -module_name <module_name>\n");
+ log(" Name of module to be created (default: "
#ifdef _WIN32
- "top" // FIXME
+ "top" // FIXME
#else
- "<filename>"
+ "<filename>"
#endif
- ")\n");
- log("\n");
- log(" -clk_name <wire_name>\n");
- log(" AIGER latches to be transformed into posedge DFFs clocked by wire of");
- log(" this name (default: clk)\n");
- log("\n");
- }
- void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
- {
- log_header(design, "Executing AIGER frontend.\n");
-
- RTLIL::IdString clk_name = "\\clk";
- RTLIL::IdString module_name;
+ ")\n");
+ log("\n");
+ log(" -clk_name <wire_name>\n");
+ log(" AIGER latches to be transformed into posedge DFFs clocked by wire of");
+ log(" this name (default: clk)\n");
+ log("\n");
+ }
+ void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing AIGER frontend.\n");
+
+ RTLIL::IdString clk_name = "\\clk";
+ RTLIL::IdString module_name;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -396,19 +433,19 @@ struct AigerFrontend : public Frontend {
}
extra_args(f, filename, args, argidx);
- if (module_name.empty()) {
+ if (module_name.empty()) {
#ifdef _WIN32
- module_name = "top"; // FIXME: basename equivalent on Win32?
+ module_name = "top"; // FIXME: basename equivalent on Win32?
#else
- char* bn = strdup(filename.c_str());
- module_name = RTLIL::escape_id(bn);
- free(bn);
+ char* bn = strdup(filename.c_str());
+ module_name = RTLIL::escape_id(bn);
+ free(bn);
#endif
- }
+ }
- AigerReader reader(design, *f, module_name, clk_name);
+ AigerReader reader(design, *f, module_name, clk_name);
reader.parse_aiger();
- }
+ }
} AigerFrontend;
YOSYS_NAMESPACE_END
diff --git a/frontends/aiger/aigerparse.h b/frontends/aiger/aigerparse.h
index c49cd152..0e3719cc 100644
--- a/frontends/aiger/aigerparse.h
+++ b/frontends/aiger/aigerparse.h
@@ -39,6 +39,7 @@ struct AigerReader
std::vector<RTLIL::Wire*> inputs;
std::vector<RTLIL::Wire*> latches;
std::vector<RTLIL::Wire*> outputs;
+ std::vector<RTLIL::Wire*> bad_properties;
AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name);
void parse_aiger();
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index d4899616..3d066af5 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -46,7 +46,7 @@ namespace AST {
// instantiate global variables (private API)
namespace AST_INTERNAL {
bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, 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_noblackbox, flag_lib, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_autowire;
AstNode *current_ast, *current_ast_mod;
std::map<std::string, AstNode*> current_scope;
const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr = NULL;
@@ -154,6 +154,7 @@ std::string AST::type2str(AstNodeType type)
X(AST_GENIF)
X(AST_GENCASE)
X(AST_GENBLOCK)
+ X(AST_TECALL)
X(AST_POSEDGE)
X(AST_NEGEDGE)
X(AST_EDGE)
@@ -194,6 +195,9 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch
is_logic = false;
is_signed = false;
is_string = false;
+ is_wand = false;
+ is_wor = false;
+ is_unsized = false;
was_checked = false;
range_valid = false;
range_swapped = false;
@@ -722,7 +726,7 @@ AstNode *AstNode::mkconst_int(uint32_t v, bool is_signed, int width)
}
// create an AST node for a constant (using a bit vector as value)
-AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed)
+AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed, bool is_unsized)
{
AstNode *node = new AstNode(AST_CONSTANT);
node->is_signed = is_signed;
@@ -736,9 +740,15 @@ AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signe
node->range_valid = true;
node->range_left = node->bits.size()-1;
node->range_right = 0;
+ node->is_unsized = is_unsized;
return node;
}
+AstNode *AstNode::mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed)
+{
+ return mkconst_bits(v, is_signed, false);
+}
+
// create an AST node for a constant (using a string in bit vector form as value)
AstNode *AstNode::mkconst_str(const std::vector<RTLIL::State> &v)
{
@@ -775,6 +785,14 @@ bool AstNode::bits_only_01() const
return true;
}
+RTLIL::Const AstNode::bitsAsUnsizedConst(int width)
+{
+ RTLIL::State extbit = bits.back();
+ while (width > int(bits.size()))
+ bits.push_back(extbit);
+ return RTLIL::Const(bits);
+}
+
RTLIL::Const AstNode::bitsAsConst(int width, bool is_signed)
{
std::vector<RTLIL::State> bits = this->bits;
@@ -942,6 +960,23 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
if (!defer)
{
+ bool blackbox_module = flag_lib;
+
+ if (!blackbox_module && !flag_noblackbox) {
+ blackbox_module = true;
+ for (auto child : ast->children) {
+ if (child->type == AST_WIRE && (child->is_input || child->is_output))
+ continue;
+ if (child->type == AST_PARAMETER || child->type == AST_LOCALPARAM)
+ continue;
+ if (child->type == AST_CELL && child->children.size() > 0 && child->children[0]->type == AST_CELLTYPE &&
+ (child->children[0]->str == "$specify2" || child->children[0]->str == "$specify3" || child->children[0]->str == "$specrule"))
+ continue;
+ blackbox_module = false;
+ break;
+ }
+ }
+
while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { }
if (flag_dump_ast2) {
@@ -956,7 +991,63 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
log("--- END OF AST DUMP ---\n");
}
- if (flag_lib) {
+ if (flag_nowb && ast->attributes.count("\\whitebox")) {
+ delete ast->attributes.at("\\whitebox");
+ ast->attributes.erase("\\whitebox");
+ }
+
+ if (ast->attributes.count("\\lib_whitebox")) {
+ if (!flag_lib || flag_nowb) {
+ delete ast->attributes.at("\\lib_whitebox");
+ ast->attributes.erase("\\lib_whitebox");
+ } else {
+ if (ast->attributes.count("\\whitebox")) {
+ delete ast->attributes.at("\\whitebox");
+ ast->attributes.erase("\\whitebox");
+ }
+ AstNode *n = ast->attributes.at("\\lib_whitebox");
+ ast->attributes["\\whitebox"] = n;
+ ast->attributes.erase("\\lib_whitebox");
+ }
+ }
+
+ if (!blackbox_module && ast->attributes.count("\\blackbox")) {
+ AstNode *n = ast->attributes.at("\\blackbox");
+ if (n->type != AST_CONSTANT)
+ log_file_error(ast->filename, ast->linenum, "Got blackbox attribute with non-constant value!\n");
+ blackbox_module = n->asBool();
+ }
+
+ if (blackbox_module && ast->attributes.count("\\whitebox")) {
+ AstNode *n = ast->attributes.at("\\whitebox");
+ if (n->type != AST_CONSTANT)
+ log_file_error(ast->filename, ast->linenum, "Got whitebox attribute with non-constant value!\n");
+ blackbox_module = !n->asBool();
+ }
+
+ if (ast->attributes.count("\\noblackbox")) {
+ if (blackbox_module) {
+ AstNode *n = ast->attributes.at("\\noblackbox");
+ if (n->type != AST_CONSTANT)
+ log_file_error(ast->filename, ast->linenum, "Got noblackbox attribute with non-constant value!\n");
+ blackbox_module = !n->asBool();
+ }
+ delete ast->attributes.at("\\noblackbox");
+ ast->attributes.erase("\\noblackbox");
+ }
+
+ if (blackbox_module)
+ {
+ if (ast->attributes.count("\\whitebox")) {
+ delete ast->attributes.at("\\whitebox");
+ ast->attributes.erase("\\whitebox");
+ }
+
+ if (ast->attributes.count("\\lib_whitebox")) {
+ delete ast->attributes.at("\\lib_whitebox");
+ ast->attributes.erase("\\lib_whitebox");
+ }
+
std::vector<AstNode*> new_children;
for (auto child : ast->children) {
if (child->type == AST_WIRE && (child->is_input || child->is_output)) {
@@ -965,12 +1056,19 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
child->delete_children();
child->children.push_back(AstNode::mkconst_int(0, false, 0));
new_children.push_back(child);
+ } else if (child->type == AST_CELL && child->children.size() > 0 && child->children[0]->type == AST_CELLTYPE &&
+ (child->children[0]->str == "$specify2" || child->children[0]->str == "$specify3" || child->children[0]->str == "$specrule")) {
+ new_children.push_back(child);
} else {
delete child;
}
}
+
ast->children.swap(new_children);
- ast->attributes["\\blackbox"] = AstNode::mkconst_int(1, false);
+
+ if (ast->attributes.count("\\blackbox") == 0) {
+ ast->attributes["\\blackbox"] = AstNode::mkconst_int(1, false);
+ }
}
ignoreThisSignalsInInitial = RTLIL::SigSpec();
@@ -1009,9 +1107,12 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
current_module->nomeminit = flag_nomeminit;
current_module->nomem2reg = flag_nomem2reg;
current_module->mem2reg = flag_mem2reg;
+ current_module->noblackbox = flag_noblackbox;
current_module->lib = flag_lib;
+ current_module->nowb = flag_nowb;
current_module->noopt = flag_noopt;
current_module->icells = flag_icells;
+ current_module->pwires = flag_pwires;
current_module->autowire = flag_autowire;
current_module->fixup_ports();
@@ -1026,7 +1127,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast
// 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 no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil,
- bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire)
+ bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire)
{
current_ast = ast;
flag_dump_ast1 = dump_ast1;
@@ -1039,9 +1140,12 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
flag_nomeminit = nomeminit;
flag_nomem2reg = nomem2reg;
flag_mem2reg = mem2reg;
+ flag_noblackbox = noblackbox;
flag_lib = lib;
+ flag_nowb = nowb;
flag_noopt = noopt;
flag_icells = icells;
+ flag_pwires = pwires;
flag_autowire = autowire;
log_assert(current_ast->type == AST_DESIGN);
@@ -1373,9 +1477,12 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString
flag_nomeminit = nomeminit;
flag_nomem2reg = nomem2reg;
flag_mem2reg = mem2reg;
+ flag_noblackbox = noblackbox;
flag_lib = lib;
+ flag_nowb = nowb;
flag_noopt = noopt;
flag_icells = icells;
+ flag_pwires = pwires;
flag_autowire = autowire;
use_internal_line_num();
@@ -1447,6 +1554,7 @@ RTLIL::Module *AstModule::clone() const
new_mod->lib = lib;
new_mod->noopt = noopt;
new_mod->icells = icells;
+ new_mod->pwires = pwires;
new_mod->autowire = autowire;
return new_mod;
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index ddd59d4b..54b2fb31 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -137,7 +137,8 @@ namespace AST
AST_GENIF,
AST_GENCASE,
AST_GENBLOCK,
-
+ AST_TECALL,
+
AST_POSEDGE,
AST_NEGEDGE,
AST_EDGE,
@@ -173,7 +174,7 @@ namespace AST
// node content - most of it is unused in most node types
std::string str;
std::vector<RTLIL::State> bits;
- bool is_input, is_output, is_reg, is_logic, is_signed, is_string, range_valid, range_swapped, was_checked;
+ bool is_input, is_output, is_reg, is_logic, is_signed, is_string, is_wand, is_wor, range_valid, range_swapped, was_checked, is_unsized;
int port_id, range_left, range_right;
uint32_t integer;
double realvalue;
@@ -262,6 +263,7 @@ namespace AST
// helper functions for creating AST nodes for constants
static AstNode *mkconst_int(uint32_t v, bool is_signed, int width = 32);
+ static AstNode *mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed, bool is_unsized);
static AstNode *mkconst_bits(const std::vector<RTLIL::State> &v, bool is_signed);
static AstNode *mkconst_str(const std::vector<RTLIL::State> &v);
static AstNode *mkconst_str(const std::string &str);
@@ -269,6 +271,7 @@ namespace AST
// helper function for creating sign-extended const objects
RTLIL::Const bitsAsConst(int width, bool is_signed);
RTLIL::Const bitsAsConst(int width = -1);
+ RTLIL::Const bitsAsUnsizedConst(int width);
RTLIL::Const asAttrConst();
RTLIL::Const asParaConst();
uint64_t asInt(bool is_signed);
@@ -283,13 +286,13 @@ namespace AST
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit,
- bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire);
+ bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire);
// parametric modules are supported directly by the AST library
// therefore we need our own derivate of RTLIL::Module with overloaded virtual functions
struct AstModule : RTLIL::Module {
AstNode *ast;
- bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire;
+ bool nolatches, nomeminit, nomem2reg, mem2reg, noblackbox, lib, nowb, noopt, icells, pwires, autowire;
~AstModule() YS_OVERRIDE;
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail) YS_OVERRIDE;
RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail) YS_OVERRIDE;
@@ -322,7 +325,7 @@ namespace AST_INTERNAL
{
// internal state variables
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_pwires, flag_autowire;
extern AST::AstNode *current_ast, *current_ast_mod;
extern std::map<std::string, AST::AstNode*> current_scope;
extern const dict<RTLIL::SigBit, RTLIL::SigBit> *genRTLIL_subst_ptr;
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index b3a2a84b..571ddd98 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -504,6 +504,7 @@ struct AST_INTERNAL::ProcessGenerator
RTLIL::CaseRule *backup_case = current_case;
current_case = new RTLIL::CaseRule;
+ current_case->attributes["\\src"] = stringf("%s:%d", child->filename.c_str(), child->linenum);
last_generated_case = current_case;
addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue);
for (auto node : child->children) {
@@ -645,6 +646,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
if (!id_ast->children[0]->range_valid)
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;
+ if (children.size() > 1)
+ range = children[1];
} else
log_file_error(filename, linenum, "Failed to detect width for identifier %s!\n", str.c_str());
if (range) {
@@ -851,7 +854,6 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
case AST_FUNCTION:
case AST_DPI_FUNCTION:
case AST_AUTOWIRE:
- case AST_LOCALPARAM:
case AST_DEFPARAM:
case AST_GENVAR:
case AST_GENFOR:
@@ -893,6 +895,26 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// remember the parameter, needed for example in techmap
case AST_PARAMETER:
current_module->avail_parameters.insert(str);
+ /* fall through */
+ case AST_LOCALPARAM:
+ if (flag_pwires)
+ {
+ if (GetSize(children) < 1 || children[0]->type != AST_CONSTANT)
+ log_file_error(filename, linenum, "Parameter `%s' with non-constant value!\n", str.c_str());
+
+ RTLIL::Const val = children[0]->bitsAsConst();
+ RTLIL::Wire *wire = current_module->addWire(str, GetSize(val));
+ current_module->connect(wire, val);
+
+ wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+ wire->attributes[type == AST_PARAMETER ? "\\parameter" : "\\localparam"] = 1;
+
+ for (auto &attr : attributes) {
+ if (attr.second->type != AST_CONSTANT)
+ log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
+ wire->attributes[attr.first] = attr.second->asAttrConst();
+ }
+ }
break;
// create an RTLIL::Wire for an AST_WIRE node
@@ -902,7 +924,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
if (!range_valid)
log_file_error(filename, linenum, "Signal `%s' with non-constant width!\n", str.c_str());
- log_assert(range_left >= range_right || (range_left == -1 && range_right == 0));
+ if (!(range_left >= range_right || (range_left == -1 && range_right == 0)))
+ log_file_error(filename, linenum, "Signal `%s' with invalid width range %d!\n", str.c_str(), range_left - range_right + 1);
RTLIL::Wire *wire = current_module->addWire(str, range_left - range_right + 1);
wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
@@ -917,6 +940,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
wire->attributes[attr.first] = attr.second->asAttrConst();
}
+
+ if (is_wand) wire->set_bool_attribute("\\wand");
+ if (is_wor) wire->set_bool_attribute("\\wor");
}
break;
@@ -961,8 +987,13 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
detectSignWidth(width_hint, sign_hint);
is_signed = sign_hint;
- if (type == AST_CONSTANT)
- return RTLIL::SigSpec(bitsAsConst());
+ if (type == AST_CONSTANT) {
+ if (is_unsized) {
+ return RTLIL::SigSpec(bitsAsUnsizedConst(width_hint));
+ } else {
+ return RTLIL::SigSpec(bitsAsConst());
+ }
+ }
RTLIL::SigSpec sig = realAsConst(width_hint);
log_file_warning(filename, linenum, "converting real value %e to binary %s.\n", realvalue, log_signal(sig));
@@ -1490,10 +1521,12 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
continue;
}
if (child->type == AST_PARASET) {
+ int extra_const_flags = 0;
IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str;
if (child->children[0]->type == AST_REALVALUE) {
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);
+ extra_const_flags = RTLIL::CONST_FLAG_REAL;
auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue));
strnode->cloneInto(child->children[0]);
delete strnode;
@@ -1502,6 +1535,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
log_file_error(filename, linenum, "Parameter %s.%s with non-constant value!\n",
log_id(cell), log_id(paraname));
cell->parameters[paraname] = child->children[0]->asParaConst();
+ cell->parameters[paraname].flags |= extra_const_flags;
continue;
}
if (child->type == AST_ARGUMENT) {
@@ -1521,9 +1555,29 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
}
for (auto &attr : attributes) {
if (attr.second->type != AST_CONSTANT)
- log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
+ log_file_error(filename, linenum, "Attribute `%s' with non-constant value.\n", attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
}
+ if (cell->type.in("$specify2", "$specify3")) {
+ int src_width = GetSize(cell->getPort("\\SRC"));
+ int dst_width = GetSize(cell->getPort("\\DST"));
+ bool full = cell->getParam("\\FULL").as_bool();
+ if (!full && src_width != dst_width)
+ log_file_error(filename, linenum, "Parallel specify SRC width does not match DST width.\n");
+ if (cell->type == "$specify3") {
+ int dat_width = GetSize(cell->getPort("\\DAT"));
+ if (dat_width != dst_width)
+ log_file_error(filename, linenum, "Specify DAT width does not match DST width.\n");
+ }
+ cell->setParam("\\SRC_WIDTH", Const(src_width));
+ cell->setParam("\\DST_WIDTH", Const(dst_width));
+ }
+ if (cell->type == "$specrule") {
+ int src_width = GetSize(cell->getPort("\\SRC"));
+ int dst_width = GetSize(cell->getPort("\\DST"));
+ cell->setParam("\\SRC_WIDTH", Const(src_width));
+ cell->setParam("\\DST_WIDTH", Const(dst_width));
+ }
}
break;
@@ -1541,6 +1595,37 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
delete always;
} break;
+ case AST_TECALL: {
+ int sz = children.size();
+ if (str == "$info") {
+ if (sz > 0)
+ log_file_info(filename, linenum, "%s.\n", children[0]->str.c_str());
+ else
+ log_file_info(filename, linenum, "\n");
+ } else if (str == "$warning") {
+ if (sz > 0)
+ log_file_warning(filename, linenum, "%s.\n", children[0]->str.c_str());
+ else
+ log_file_warning(filename, linenum, "\n");
+ } else if (str == "$error") {
+ if (sz > 0)
+ log_file_error(filename, linenum, "%s.\n", children[0]->str.c_str());
+ else
+ log_file_error(filename, linenum, "\n");
+ } else if (str == "$fatal") {
+ // TODO: 1st parameter, if exists, is 0,1 or 2, and passed to $finish()
+ // if no parameter is given, default value is 1
+ // dollar_finish(sz ? children[0] : 1);
+ // perhaps create & use log_file_fatal()
+ if (sz > 0)
+ log_file_error(filename, linenum, "FATAL: %s.\n", children[0]->str.c_str());
+ else
+ log_file_error(filename, linenum, "FATAL.\n");
+ } else {
+ log_file_error(filename, linenum, "Unknown elabortoon system task '%s'.\n", str.c_str());
+ }
+ } break;
+
case AST_FCALL: {
if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq")
{
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 63b71b80..e947125b 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -1030,7 +1030,26 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
log_file_error(filename, linenum, "While loops are only allowed in constant functions!\n");
if (type == AST_REPEAT)
- log_file_error(filename, linenum, "Repeat loops are only allowed in constant functions!\n");
+ {
+ AstNode *count = children[0];
+ AstNode *body = children[1];
+
+ // eval count expression
+ while (count->simplify(true, false, false, stage, 32, true, false)) { }
+
+ if (count->type != AST_CONSTANT)
+ log_file_error(filename, linenum, "Repeat loops outside must have constant repeat counts!\n");
+
+ // convert to a block with the body repeated n times
+ type = AST_BLOCK;
+ children.clear();
+ for (int i = 0; i < count->bitsAsConst().as_int(); i++)
+ children.insert(children.begin(), body->clone());
+
+ delete count;
+ delete body;
+ did_something = true;
+ }
// unroll for loops and generate-for blocks
if ((type == AST_GENFOR || type == AST_FOR) && children.size() != 0)
@@ -1066,7 +1085,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
// eval 1st expression
AstNode *varbuf = init_ast->children[1]->clone();
- while (varbuf->simplify(true, false, false, stage, 32, true, false)) { }
+ {
+ int expr_width_hint = -1;
+ bool expr_sign_hint = true;
+ varbuf->detectSignWidth(expr_width_hint, expr_sign_hint);
+ while (varbuf->simplify(true, false, false, stage, 32, true, false)) { }
+ }
if (varbuf->type != AST_CONSTANT)
log_file_error(filename, linenum, "Right hand side of 1st expression of generate for-loop is not constant!\n");
@@ -1088,7 +1112,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
{
// eval 2nd expression
AstNode *buf = while_ast->clone();
- while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
+ {
+ int expr_width_hint = -1;
+ bool expr_sign_hint = true;
+ buf->detectSignWidth(expr_width_hint, expr_sign_hint);
+ while (buf->simplify(true, false, false, stage, expr_width_hint, expr_sign_hint, false)) { }
+ }
if (buf->type != AST_CONSTANT)
log_file_error(filename, linenum, "2nd expression of generate for-loop is not constant!\n");
@@ -1129,7 +1158,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
// eval 3rd expression
buf = next_ast->children[1]->clone();
- while (buf->simplify(true, false, false, stage, 32, true, false)) { }
+ {
+ int expr_width_hint = -1;
+ bool expr_sign_hint = true;
+ buf->detectSignWidth(expr_width_hint, expr_sign_hint);
+ while (buf->simplify(true, false, false, stage, expr_width_hint, expr_sign_hint, true)) { }
+ }
if (buf->type != AST_CONSTANT)
log_file_error(filename, linenum, "Right hand side of 3rd expression of generate for-loop is not constant!\n");
@@ -1138,6 +1172,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
varbuf->children[0] = buf;
}
+ if (type == AST_FOR) {
+ AstNode *buf = next_ast->clone();
+ delete buf->children[1];
+ buf->children[1] = varbuf->children[0]->clone();
+ current_block->children.insert(current_block->children.begin() + current_block_idx++, buf);
+ }
+
current_scope[varbuf->str] = backup_scope_varbuf;
delete varbuf;
delete_children();
@@ -1564,6 +1605,7 @@ skip_dynamic_range_lvalue_expansion:;
current_scope[wire_tmp->str] = wire_tmp;
wire_tmp->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
while (wire_tmp->simplify(true, false, false, 1, -1, false, false)) { }
+ wire_tmp->is_logic = true;
AstNode *wire_tmp_id = new AstNode(AST_IDENTIFIER);
wire_tmp_id->str = wire_tmp->str;
diff --git a/frontends/ilang/ilang_frontend.cc b/frontends/ilang/ilang_frontend.cc
index 6b302a79..30d9ff79 100644
--- a/frontends/ilang/ilang_frontend.cc
+++ b/frontends/ilang/ilang_frontend.cc
@@ -47,16 +47,20 @@ struct IlangFrontend : public Frontend {
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(" 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");
+ log(" -lib\n");
+ log(" only create empty blackbox modules\n");
+ log("\n");
}
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;
+ ILANG_FRONTEND::flag_lib = false;
log_header(design, "Executing ILANG frontend.\n");
@@ -73,6 +77,10 @@ struct IlangFrontend : public Frontend {
ILANG_FRONTEND::flag_overwrite = true;
continue;
}
+ if (arg == "-lib") {
+ ILANG_FRONTEND::flag_lib = true;
+ continue;
+ }
break;
}
extra_args(f, filename, args, argidx);
diff --git a/frontends/ilang/ilang_frontend.h b/frontends/ilang/ilang_frontend.h
index 052dd4cb..f8a15284 100644
--- a/frontends/ilang/ilang_frontend.h
+++ b/frontends/ilang/ilang_frontend.h
@@ -34,6 +34,7 @@ namespace ILANG_FRONTEND {
extern RTLIL::Design *current_design;
extern bool flag_nooverwrite;
extern bool flag_overwrite;
+ extern bool flag_lib;
}
YOSYS_NAMESPACE_END
diff --git a/frontends/ilang/ilang_lexer.l b/frontends/ilang/ilang_lexer.l
index d8e01ae4..4fd0ae85 100644
--- a/frontends/ilang/ilang_lexer.l
+++ b/frontends/ilang/ilang_lexer.l
@@ -53,6 +53,7 @@ USING_YOSYS_NAMESPACE
"attribute" { return TOK_ATTRIBUTE; }
"parameter" { return TOK_PARAMETER; }
"signed" { return TOK_SIGNED; }
+"real" { return TOK_REAL; }
"wire" { return TOK_WIRE; }
"memory" { return TOK_MEMORY; }
"width" { return TOK_WIDTH; }
diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y
index 5bcc01f4..b4b9693d 100644
--- a/frontends/ilang/ilang_parser.y
+++ b/frontends/ilang/ilang_parser.y
@@ -37,7 +37,7 @@ namespace ILANG_FRONTEND {
std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack;
std::vector<RTLIL::CaseRule*> case_stack;
dict<RTLIL::IdString, RTLIL::Const> attrbuf;
- bool flag_nooverwrite, flag_overwrite;
+ bool flag_nooverwrite, flag_overwrite, flag_lib;
bool delete_current_module;
}
using namespace ILANG_FRONTEND;
@@ -45,7 +45,16 @@ YOSYS_NAMESPACE_END
USING_YOSYS_NAMESPACE
%}
-%name-prefix "rtlil_frontend_ilang_yy"
+%define api.prefix {rtlil_frontend_ilang_yy}
+
+/* The union is defined in the header, so we need to provide all the
+ * includes it requires
+ */
+%code requires {
+#include <string>
+#include <vector>
+#include "frontends/ilang/ilang_frontend.h"
+}
%union {
char *string;
@@ -61,7 +70,7 @@ USING_YOSYS_NAMESPACE
%token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC
%token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS TOK_GLOBAL TOK_INIT
%token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET
-%token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_UPTO
+%token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_REAL TOK_UPTO
%type <rsigspec> sigspec_list_reversed
%type <sigspec> sigspec sigspec_list
@@ -98,7 +107,7 @@ module:
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()) {
+ if (!flag_overwrite && (flag_lib || (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")) {
@@ -124,6 +133,8 @@ module:
current_module->fixup_ports();
if (delete_current_module)
delete current_module;
+ else if (flag_lib)
+ current_module->makeblackbox();
current_module = nullptr;
} EOL;
@@ -239,6 +250,12 @@ cell_body:
free($4);
delete $5;
} |
+ cell_body TOK_PARAMETER TOK_REAL TOK_ID constant EOL {
+ current_cell->parameters[$4] = *$5;
+ current_cell->parameters[$4].flags |= RTLIL::CONST_FLAG_REAL;
+ free($4);
+ delete $5;
+ } |
cell_body TOK_CONNECT TOK_ID sigspec EOL {
if (current_cell->hasPort($3))
rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of cell port %s.", $3).c_str());
@@ -265,14 +282,14 @@ proc_stmt:
} case_body sync_list TOK_END EOL;
switch_stmt:
- attr_list TOK_SWITCH sigspec EOL {
+ TOK_SWITCH sigspec EOL {
RTLIL::SwitchRule *rule = new RTLIL::SwitchRule;
- rule->signal = *$3;
+ rule->signal = *$2;
rule->attributes = attrbuf;
switch_stack.back()->push_back(rule);
attrbuf.clear();
- delete $3;
- } switch_body TOK_END EOL;
+ delete $2;
+ } attr_list switch_body TOK_END EOL;
attr_list:
/* empty */ |
@@ -281,9 +298,11 @@ attr_list:
switch_body:
switch_body TOK_CASE {
RTLIL::CaseRule *rule = new RTLIL::CaseRule;
+ rule->attributes = attrbuf;
switch_stack.back()->back()->cases.push_back(rule);
switch_stack.push_back(&rule->switches);
case_stack.push_back(rule);
+ attrbuf.clear();
} compare_list EOL case_body {
switch_stack.pop_back();
case_stack.pop_back();
@@ -302,12 +321,15 @@ compare_list:
/* empty */;
case_body:
+ case_body attr_stmt |
case_body switch_stmt |
case_body assign_stmt |
/* empty */;
assign_stmt:
TOK_ASSIGN sigspec sigspec EOL {
+ if (attrbuf.size() != 0)
+ rtlil_frontend_ilang_yyerror("dangling attribute");
case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3));
delete $2;
delete $3;
@@ -443,4 +465,3 @@ conn_stmt:
delete $2;
delete $3;
};
-
diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc
index 82361ea9..f5ae8eb7 100644
--- a/frontends/json/jsonparse.cc
+++ b/frontends/json/jsonparse.cc
@@ -292,6 +292,18 @@ void json_import(Design *design, string &modname, JsonNode *node)
if (port_wire == nullptr)
port_wire = module->addWire(port_name, GetSize(port_bits_node->data_array));
+ if (port_node->data_dict.count("upto") != 0) {
+ JsonNode *val = port_node->data_dict.at("upto");
+ if (val->type == 'N')
+ port_wire->upto = val->data_number != 0;
+ }
+
+ if (port_node->data_dict.count("offset") != 0) {
+ JsonNode *val = port_node->data_dict.at("offset");
+ if (val->type == 'N')
+ port_wire->start_offset = val->data_number;
+ }
+
if (port_direction_node->data_string == "input") {
port_wire->port_input = true;
} else
@@ -372,6 +384,18 @@ void json_import(Design *design, string &modname, JsonNode *node)
if (wire == nullptr)
wire = module->addWire(net_name, GetSize(bits_node->data_array));
+ if (net_node->data_dict.count("upto") != 0) {
+ JsonNode *val = net_node->data_dict.at("upto");
+ if (val->type == 'N')
+ wire->upto = val->data_number != 0;
+ }
+
+ if (net_node->data_dict.count("offset") != 0) {
+ JsonNode *val = net_node->data_dict.at("offset");
+ if (val->type == 'N')
+ wire->start_offset = val->data_number;
+ }
+
for (int i = 0; i < GetSize(bits_node->data_array); i++)
{
JsonNode *bitval_node = bits_node->data_array.at(i);
diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc
index ed9727b8..2bf99e58 100644
--- a/frontends/verific/verific.cc
+++ b/frontends/verific/verific.cc
@@ -46,7 +46,15 @@ USING_YOSYS_NAMESPACE
#include "VeriModule.h"
#include "VeriWrite.h"
#include "VhdlUnits.h"
-#include "Message.h"
+#include "VeriLibrary.h"
+
+#ifndef SYMBIOTIC_VERIFIC_API_VERSION
+# error "Only Symbiotic EDA flavored Verific is supported. Please contact office@symbioticeda.com for commercial support for Yosys+Verific."
+#endif
+
+#if SYMBIOTIC_VERIFIC_API_VERSION < 1
+# error "Please update your version of Symbiotic EDA flavored Verific."
+#endif
#ifdef __clang__
#pragma clang diagnostic pop
@@ -776,13 +784,14 @@ void VerificImporter::merge_past_ffs(pool<RTLIL::Cell*> &candidates)
void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set<Netlist*> &nl_todo)
{
- std::string module_name = nl->IsOperator() ? std::string("$verific$") + nl->Owner()->Name() : RTLIL::escape_id(nl->Owner()->Name());
+ std::string netlist_name = nl->GetAtt(" \\top") ? nl->CellBaseName() : nl->Owner()->Name();
+ std::string module_name = nl->IsOperator() ? "$verific$" + netlist_name : RTLIL::escape_id(netlist_name);
netlist = nl;
if (design->has(module_name)) {
if (!nl->IsOperator() && !is_blackbox(nl))
- log_cmd_error("Re-definition of module `%s'.\n", nl->Owner()->Name());
+ log_cmd_error("Re-definition of module `%s'.\n", netlist_name.c_str());
return;
}
@@ -1752,32 +1761,64 @@ struct VerificExtNets
}
};
-void verific_import(Design *design, std::string top)
+void verific_import(Design *design, const std::map<std::string,std::string> &parameters, std::string top)
{
verific_sva_fsm_limit = 16;
std::set<Netlist*> nl_todo, nl_done;
- {
- VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1);
- VeriLibrary *veri_lib = veri_file::GetLibrary("work", 1);
+ VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1);
+ VeriLibrary *veri_lib = veri_file::GetLibrary("work", 1);
+ Array *netlists = NULL;
+ Array veri_libs, vhdl_libs;
+ if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib);
+ if (veri_lib) veri_libs.InsertLast(veri_lib);
- Array veri_libs, vhdl_libs;
- if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib);
- if (veri_lib) veri_libs.InsertLast(veri_lib);
+ Map verific_params(STRING_HASH);
+ for (const auto &i : parameters)
+ verific_params.Insert(i.first.c_str(), i.second.c_str());
- Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs);
- Netlist *nl;
- int i;
+ if (top.empty()) {
+ netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, &verific_params);
+ }
+ else {
+ Array veri_modules, vhdl_units;
+
+ if (veri_lib) {
+ VeriModule *veri_module = veri_lib->GetModule(top.c_str(), 1);
+ if (veri_module) {
+ veri_modules.InsertLast(veri_module);
+ }
+
+ // Also elaborate all root modules since they may contain bind statements
+ MapIter mi;
+ FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) {
+ if (!veri_module->IsRootModule()) continue;
+ veri_modules.InsertLast(veri_module);
+ }
+ }
- FOREACH_ARRAY_ITEM(netlists, i, nl) {
- if (top.empty() || nl->Owner()->Name() == top)
- nl_todo.insert(nl);
+ if (vhdl_lib) {
+ VhdlDesignUnit *vhdl_unit = vhdl_lib->GetPrimUnit(top.c_str());
+ if (vhdl_unit)
+ vhdl_units.InsertLast(vhdl_unit);
}
- delete netlists;
+ netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, &verific_params);
}
+ Netlist *nl;
+ int i;
+
+ FOREACH_ARRAY_ITEM(netlists, i, nl) {
+ if (top.empty() && nl->CellBaseName() != top)
+ continue;
+ nl->AddAtt(new Att(" \\top", NULL));
+ nl_todo.insert(nl);
+ }
+
+ delete netlists;
+
if (!verific_error_msg.empty())
log_error("%s\n", verific_error_msg.c_str());
@@ -1983,6 +2024,9 @@ struct VerificPass : public Pass {
// WARNING: instantiating unknown module 'XYZ' (VERI-1063)
Message::SetMessageType("VERI-1063", VERIFIC_ERROR);
+ // https://github.com/YosysHQ/yosys/issues/1055
+ RuntimeFlags::SetVar("veri_elaborate_top_level_modules_having_interface_ports", 1) ;
+
#ifndef DB_PRESERVE_INITIAL_VALUE
# warning Verific was built without DB_PRESERVE_INITIAL_VALUE.
#endif
@@ -2212,8 +2256,8 @@ struct VerificPass : public Pass {
continue;
}
if (args[argidx] == "-chparam" && argidx+2 < GetSize(args)) {
- const std::string &key = args[++argidx];
- const std::string &value = args[++argidx];
+ const std::string &key = args[++argidx];
+ const std::string &value = args[++argidx];
unsigned new_insertion = parameters.Insert(key.c_str(), value.c_str(),
1 /* force_overwrite */);
if (!new_insertion)
@@ -2270,12 +2314,22 @@ struct VerificPass : public Pass {
for (; argidx < GetSize(args); argidx++)
{
const char *name = args[argidx].c_str();
+ VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1);
+
+ if (veri_lib) {
+ VeriModule *veri_module = veri_lib->GetModule(name, 1);
+ if (veri_module) {
+ log("Adding Verilog module '%s' to elaboration queue.\n", name);
+ veri_modules.InsertLast(veri_module);
+ continue;
+ }
- VeriModule *veri_module = veri_file::GetModule(name);
- if (veri_module) {
- log("Adding Verilog module '%s' to elaboration queue.\n", name);
- veri_modules.InsertLast(veri_module);
- continue;
+ // Also elaborate all root modules since they may contain bind statements
+ MapIter mi;
+ FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) {
+ if (!veri_module->IsRootModule()) continue;
+ veri_modules.InsertLast(veri_module);
+ }
}
VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1);
@@ -2294,8 +2348,10 @@ struct VerificPass : public Pass {
Netlist *nl;
int i;
- FOREACH_ARRAY_ITEM(netlists, i, nl)
+ FOREACH_ARRAY_ITEM(netlists, i, nl) {
+ nl->AddAtt(new Att(" \\top", NULL));
nl_todo.insert(nl);
+ }
delete netlists;
}
diff --git a/frontends/verific/verific.h b/frontends/verific/verific.h
index b331dd4b..88a6cc0b 100644
--- a/frontends/verific/verific.h
+++ b/frontends/verific/verific.h
@@ -26,7 +26,7 @@ YOSYS_NAMESPACE_BEGIN
extern int verific_verbose;
extern bool verific_import_pending;
-extern void verific_import(Design *design, std::string top = std::string());
+extern void verific_import(Design *design, const std::map<std::string,std::string> &parameters, std::string top = std::string());
extern pool<int> verific_sva_prims;
diff --git a/frontends/verilog/Makefile.inc b/frontends/verilog/Makefile.inc
index dbaace58..6a8462b4 100644
--- a/frontends/verilog/Makefile.inc
+++ b/frontends/verilog/Makefile.inc
@@ -14,6 +14,8 @@ frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l
$(Q) mkdir -p $(dir $@)
$(P) flex -o frontends/verilog/verilog_lexer.cc $<
+frontends/verilog/verilog_parser.tab.o: CXXFLAGS += -DYYMAXDEPTH=10000000
+
OBJS += frontends/verilog/verilog_parser.tab.o
OBJS += frontends/verilog/verilog_lexer.o
OBJS += frontends/verilog/preproc.o
diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc
index 7848c626..f6a17b24 100644
--- a/frontends/verilog/const2ast.cc
+++ b/frontends/verilog/const2ast.cc
@@ -71,7 +71,7 @@ static int my_ilog2(int x)
}
// parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?')
-static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type)
+static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type, bool is_unsized)
{
// all digits in string (MSB at index 0)
std::vector<uint8_t> digits;
@@ -129,6 +129,9 @@ static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int le
return;
}
+ if (is_unsized && (len > len_in_bits))
+ log_file_error(current_filename, get_line_num(), "Unsized constant must have width of 1 bit, but have %d bits!\n", len);
+
for (len = len - 1; len >= 0; len--)
if (data[len] == RTLIL::S1)
break;
@@ -150,7 +153,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
{
if (warn_z) {
AstNode *ret = const2ast(code, case_type);
- if (std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end())
+ if (ret != nullptr && std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end())
log_warning("Yosys has only limited support for tri-state logic at the moment. (%s:%d)\n",
current_filename.c_str(), get_line_num());
return ret;
@@ -186,7 +189,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
// Simple base-10 integer
if (*endptr == 0) {
std::vector<RTLIL::State> data;
- my_strtobin(data, str, -1, 10, case_type);
+ my_strtobin(data, str, -1, 10, case_type, false);
if (data.back() == RTLIL::S1)
data.push_back(RTLIL::S0);
return AstNode::mkconst_bits(data, true);
@@ -201,6 +204,7 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
{
std::vector<RTLIL::State> data;
bool is_signed = false;
+ bool is_unsized = len_in_bits < 0;
if (*(endptr+1) == 's') {
is_signed = true;
endptr++;
@@ -209,28 +213,34 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn
{
case 'b':
case 'B':
- my_strtobin(data, endptr+2, len_in_bits, 2, case_type);
+ my_strtobin(data, endptr+2, len_in_bits, 2, case_type, is_unsized);
break;
case 'o':
case 'O':
- my_strtobin(data, endptr+2, len_in_bits, 8, case_type);
+ my_strtobin(data, endptr+2, len_in_bits, 8, case_type, is_unsized);
break;
case 'd':
case 'D':
- my_strtobin(data, endptr+2, len_in_bits, 10, case_type);
+ my_strtobin(data, endptr+2, len_in_bits, 10, case_type, is_unsized);
break;
case 'h':
case 'H':
- my_strtobin(data, endptr+2, len_in_bits, 16, case_type);
+ my_strtobin(data, endptr+2, len_in_bits, 16, case_type, is_unsized);
break;
default:
- return NULL;
+ char next_char = char(tolower(*(endptr+1)));
+ if (next_char == '0' || next_char == '1' || next_char == 'x' || next_char == 'z') {
+ is_unsized = true;
+ my_strtobin(data, endptr+1, 1, 2, case_type, is_unsized);
+ } else {
+ return NULL;
+ }
}
if (len_in_bits < 0) {
if (is_signed && data.back() == RTLIL::S1)
data.push_back(RTLIL::S0);
}
- return AstNode::mkconst_bits(data, is_signed);
+ return AstNode::mkconst_bits(data, is_signed, is_unsized);
}
return NULL;
diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc
index 504f8b3f..0e2bead6 100644
--- a/frontends/verilog/verilog_frontend.cc
+++ b/frontends/verilog/verilog_frontend.cc
@@ -145,8 +145,21 @@ struct VerilogFrontend : public Frontend {
log(" -nodpi\n");
log(" disable DPI-C support\n");
log("\n");
+ log(" -noblackbox\n");
+ log(" do not automatically add a (* blackbox *) attribute to an\n");
+ log(" empty module.\n");
+ log("\n");
log(" -lib\n");
log(" only create empty blackbox modules. This implies -DBLACKBOX.\n");
+ log(" modules with the (* whitebox *) attribute will be preserved.\n");
+ log(" (* lib_whitebox *) will be treated like (* whitebox *).\n");
+ log("\n");
+ log(" -nowb\n");
+ log(" delete (* whitebox *) and (* lib_whitebox *) attributes from\n");
+ log(" all modules.\n");
+ log("\n");
+ log(" -specify\n");
+ log(" parse and import specify blocks\n");
log("\n");
log(" -noopt\n");
log(" don't perform basic optimizations (such as const folding) in the\n");
@@ -155,6 +168,9 @@ struct VerilogFrontend : public Frontend {
log(" -icells\n");
log(" interpret cell types starting with '$' as internal cell types\n");
log("\n");
+ log(" -pwires\n");
+ log(" add a wire for each module parameter\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 black box\n");
@@ -215,9 +231,12 @@ struct VerilogFrontend : public Frontend {
bool flag_nodpi = false;
bool flag_noopt = false;
bool flag_icells = false;
+ bool flag_pwires = false;
bool flag_nooverwrite = false;
bool flag_overwrite = false;
bool flag_defer = false;
+ bool flag_noblackbox = false;
+ bool flag_nowb = false;
std::map<std::string, std::string> defines_map;
std::list<std::string> include_dirs;
std::list<std::string> attributes;
@@ -228,10 +247,9 @@ struct VerilogFrontend : public Frontend {
norestrict_mode = false;
assume_asserts_mode = false;
lib_mode = false;
+ specify_mode = false;
default_nettype_wire = true;
- log_header(design, "Executing Verilog-2005 frontend.\n");
-
args.insert(args.begin()+1, verilog_defaults.begin(), verilog_defaults.end());
size_t argidx;
@@ -329,11 +347,23 @@ struct VerilogFrontend : public Frontend {
flag_nodpi = true;
continue;
}
+ if (arg == "-noblackbox") {
+ flag_noblackbox = true;
+ continue;
+ }
if (arg == "-lib") {
lib_mode = true;
defines_map["BLACKBOX"] = string();
continue;
}
+ if (arg == "-nowb") {
+ flag_nowb = true;
+ continue;
+ }
+ if (arg == "-specify") {
+ specify_mode = true;
+ continue;
+ }
if (arg == "-noopt") {
flag_noopt = true;
continue;
@@ -342,6 +372,10 @@ struct VerilogFrontend : public Frontend {
flag_icells = true;
continue;
}
+ if (arg == "-pwires") {
+ flag_pwires = true;
+ continue;
+ }
if (arg == "-ignore_redef" || arg == "-nooverwrite") {
flag_nooverwrite = true;
flag_overwrite = false;
@@ -395,6 +429,8 @@ struct VerilogFrontend : public Frontend {
}
extra_args(f, filename, args, argidx);
+ log_header(design, "Executing Verilog-2005 frontend: %s\n", filename.c_str());
+
log("Parsing %s%s input from `%s' to AST representation.\n",
formal_mode ? "formal " : "", sv_mode ? "SystemVerilog" : "Verilog", filename.c_str());
@@ -429,7 +465,8 @@ struct VerilogFrontend : public Frontend {
if (flag_nodpi)
error_on_dpi_function(current_ast);
- AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, lib_mode, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire);
+ AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches,
+ flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, lib_mode, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire);
if (!flag_nopp)
delete lexin;
diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h
index 523bbc89..a7c9b2fe 100644
--- a/frontends/verilog/verilog_frontend.h
+++ b/frontends/verilog/verilog_frontend.h
@@ -72,6 +72,9 @@ namespace VERILOG_FRONTEND
// running in -lib mode
extern bool lib_mode;
+ // running in -specify mode
+ extern bool specify_mode;
+
// lexer input stream
extern std::istream *lexin;
}
diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l
index 6ef38252..951d9c66 100644
--- a/frontends/verilog/verilog_lexer.l
+++ b/frontends/verilog/verilog_lexer.l
@@ -148,7 +148,7 @@ YOSYS_NAMESPACE_END
"endfunction" { return TOK_ENDFUNCTION; }
"task" { return TOK_TASK; }
"endtask" { return TOK_ENDTASK; }
-"specify" { return TOK_SPECIFY; }
+"specify" { return specify_mode ? TOK_SPECIFY : TOK_IGNORED_SPECIFY; }
"endspecify" { return TOK_ENDSPECIFY; }
"specparam" { return TOK_SPECPARAM; }
"package" { SV_KEYWORD(TOK_PACKAGE); }
@@ -193,6 +193,8 @@ YOSYS_NAMESPACE_END
to fix parsing of cells otherwise. (the current cell parser forces a reduce very early to update some
global state.. its a mess) */
[a-zA-Z_$][a-zA-Z0-9_$]*/[ \t\r\n]*:[ \t\r\n]*(assert|assume|cover|restrict)[^a-zA-Z0-9_$\.] {
+ if (!strcmp(yytext, "default"))
+ return TOK_DEFAULT;
frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext);
return TOK_SVA_LABEL;
}
@@ -206,7 +208,9 @@ YOSYS_NAMESPACE_END
"const" { if (formal_mode) return TOK_CONST; SV_KEYWORD(TOK_CONST); }
"checker" { if (formal_mode) return TOK_CHECKER; SV_KEYWORD(TOK_CHECKER); }
"endchecker" { if (formal_mode) return TOK_ENDCHECKER; SV_KEYWORD(TOK_ENDCHECKER); }
+"final" { SV_KEYWORD(TOK_FINAL); }
"logic" { SV_KEYWORD(TOK_LOGIC); }
+"var" { SV_KEYWORD(TOK_VAR); }
"bit" { SV_KEYWORD(TOK_REG); }
"eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); }
@@ -216,6 +220,8 @@ YOSYS_NAMESPACE_END
"output" { return TOK_OUTPUT; }
"inout" { return TOK_INOUT; }
"wire" { return TOK_WIRE; }
+"wor" { return TOK_WOR; }
+"wand" { return TOK_WAND; }
"reg" { return TOK_REG; }
"integer" { return TOK_INTEGER; }
"signed" { return TOK_SIGNED; }
@@ -230,7 +236,7 @@ YOSYS_NAMESPACE_END
return TOK_CONSTVAL;
}
-[0-9]*[ \t]*\'s?[bodhBODH][ \t\r\n]*[0-9a-fA-FzxZX?_]+ {
+[0-9]*[ \t]*\'s?[bodhBODH]*[ \t\r\n]*[0-9a-fA-FzxZX?_]+ {
frontend_verilog_yylval.string = new std::string(yytext);
return TOK_CONSTVAL;
}
@@ -301,6 +307,17 @@ supply1 { return TOK_SUPPLY1; }
return TOK_ID;
}
+"$"(setup|hold|setuphold|removal|recovery|recrem|skew|timeskew|fullskew|nochange) {
+ if (!specify_mode) REJECT;
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_ID;
+}
+
+"$"(info|warning|error|fatal) {
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_MSG_TASKS;
+}
+
"$signed" { return TOK_TO_SIGNED; }
"$unsigned" { return TOK_TO_UNSIGNED; }
@@ -411,6 +428,17 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ {
"+:" { return TOK_POS_INDEXED; }
"-:" { return TOK_NEG_INDEXED; }
+[-+]?[=*]> {
+ if (!specify_mode) REJECT;
+ frontend_verilog_yylval.string = new std::string(yytext);
+ return TOK_SPECIFY_OPER;
+}
+
+"&&&" {
+ if (!specify_mode) REJECT;
+ return TOK_SPECIFY_AND;
+}
+
"/*" { BEGIN(COMMENT); }
<COMMENT>. /* ignore comment body */
<COMMENT>\n /* ignore comment body */
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
index 52685f63..0fec445f 100644
--- a/frontends/verilog/verilog_parser.y
+++ b/frontends/verilog/verilog_parser.y
@@ -59,7 +59,7 @@ namespace VERILOG_FRONTEND {
std::vector<char> case_type_stack;
bool do_not_require_port_stubs;
bool default_nettype_wire;
- bool sv_mode, formal_mode, lib_mode;
+ bool sv_mode, formal_mode, lib_mode, specify_mode;
bool noassert_mode, noassume_mode, norestrict_mode;
bool assume_asserts_mode, assert_assumes_mode;
bool current_wire_rand, current_wire_const;
@@ -94,29 +94,58 @@ static void free_attr(std::map<std::string, AstNode*> *al)
delete al;
}
+struct specify_target {
+ char polarity_op;
+ AstNode *dst, *dat;
+};
+
+struct specify_triple {
+ AstNode *t_min, *t_avg, *t_max;
+};
+
+struct specify_rise_fall {
+ specify_triple rise;
+ specify_triple fall;
+};
+
%}
-%name-prefix "frontend_verilog_yy"
+%define api.prefix {frontend_verilog_yy}
+
+/* The union is defined in the header, so we need to provide all the
+ * includes it requires
+ */
+%code requires {
+#include <map>
+#include <string>
+#include "frontends/verilog/verilog_frontend.h"
+}
%union {
std::string *string;
struct YOSYS_NAMESPACE_PREFIX AST::AstNode *ast;
std::map<std::string, YOSYS_NAMESPACE_PREFIX AST::AstNode*> *al;
+ struct specify_target *specify_target_ptr;
+ struct specify_triple *specify_triple_ptr;
+ struct specify_rise_fall *specify_rise_fall_ptr;
bool boolean;
+ char ch;
}
-%token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE TOK_SVA_LABEL
-%token TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER
+%token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE
+%token <string> TOK_SVA_LABEL TOK_SPECIFY_OPER TOK_MSG_TASKS
+%token TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER TOK_FINAL
%token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END
%token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM
%token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP
-%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT
-%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG TOK_LOGIC
+%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR
+%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_WAND TOK_WOR TOK_REG TOK_LOGIC
%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL
%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT
%token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR TOK_AUTOMATIC
%token TOK_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT
-%token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK TOK_SPECIFY TOK_ENDSPECIFY TOK_SPECPARAM
+%token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK TOK_SPECIFY
+%token TOK_IGNORED_SPECIFY TOK_ENDSPECIFY TOK_SPECPARAM TOK_SPECIFY_AND
%token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR TOK_REAL
%token TOK_SYNOPSYS_FULL_CASE TOK_SYNOPSYS_PARALLEL_CASE
%token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED
@@ -130,6 +159,12 @@ static void free_attr(std::map<std::string, AstNode*> *al)
%type <boolean> opt_signed opt_property unique_case_attr
%type <al> attr case_attr
+%type <specify_target_ptr> specify_target
+%type <specify_triple_ptr> specify_triple
+%type <specify_rise_fall_ptr> specify_rise_fall
+%type <ast> specify_if specify_condition specify_opt_arg
+%type <ch> specify_edge
+
// operator precedence from low to high
%left OP_LOR
%left OP_LAND
@@ -284,15 +319,17 @@ module_para_list:
single_module_para:
/* empty */ |
- TOK_PARAMETER {
+ attr TOK_PARAMETER {
if (astbuf1) delete astbuf1;
astbuf1 = new AstNode(AST_PARAMETER);
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
+ append_attr(astbuf1, $1);
} param_signed param_integer param_range single_param_decl |
- TOK_LOCALPARAM {
+ attr TOK_LOCALPARAM {
if (astbuf1) delete astbuf1;
astbuf1 = new AstNode(AST_LOCALPARAM);
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
+ append_attr(astbuf1, $1);
} param_signed param_integer param_range single_param_decl |
single_param_decl;
@@ -310,7 +347,13 @@ module_arg_opt_assignment:
if (ast_stack.back()->children.size() > 0 && ast_stack.back()->children.back()->type == AST_WIRE) {
AstNode *wire = new AstNode(AST_IDENTIFIER);
wire->str = ast_stack.back()->children.back()->str;
- if (ast_stack.back()->children.back()->is_reg)
+ if (ast_stack.back()->children.back()->is_input) {
+ AstNode *n = ast_stack.back()->children.back();
+ if (n->attributes.count("\\defaultvalue"))
+ delete n->attributes.at("\\defaultvalue");
+ n->attributes["\\defaultvalue"] = $2;
+ } else
+ if (ast_stack.back()->children.back()->is_reg || ast_stack.back()->children.back()->is_logic)
ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $2))));
else
ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2));
@@ -450,12 +493,21 @@ wire_type_token_io:
wire_type_token:
TOK_WIRE {
} |
+ TOK_WOR {
+ astbuf3->is_wor = true;
+ } |
+ TOK_WAND {
+ astbuf3->is_wand = true;
+ } |
TOK_REG {
astbuf3->is_reg = true;
} |
TOK_LOGIC {
astbuf3->is_logic = true;
} |
+ TOK_VAR {
+ astbuf3->is_logic = true;
+ } |
TOK_INTEGER {
astbuf3->is_reg = true;
astbuf3->range_left = 31;
@@ -465,6 +517,7 @@ wire_type_token:
TOK_GENVAR {
astbuf3->type = AST_GENVAR;
astbuf3->is_reg = true;
+ astbuf3->is_signed = true;
astbuf3->range_left = 31;
astbuf3->range_right = 0;
} |
@@ -539,7 +592,7 @@ module_body:
module_body_stmt:
task_func_decl | specify_block |param_decl | localparam_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt |
- always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl;
+ always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block;
checker_decl:
TOK_CHECKER TOK_ID ';' {
@@ -697,15 +750,254 @@ task_func_body:
task_func_body behavioral_stmt |
/* empty */;
+/*************************** specify parser ***************************/
+
specify_block:
- TOK_SPECIFY specify_item_opt TOK_ENDSPECIFY |
- TOK_SPECIFY TOK_ENDSPECIFY ;
+ TOK_SPECIFY specify_item_list TOK_ENDSPECIFY;
-specify_item_opt:
- specify_item_opt specify_item |
- specify_item ;
+specify_item_list:
+ specify_item specify_item_list |
+ /* empty */;
specify_item:
+ specify_if '(' specify_edge expr TOK_SPECIFY_OPER specify_target ')' '=' specify_rise_fall ';' {
+ AstNode *en_expr = $1;
+ char specify_edge = $3;
+ AstNode *src_expr = $4;
+ string *oper = $5;
+ specify_target *target = $6;
+ specify_rise_fall *timing = $9;
+
+ if (specify_edge != 0 && target->dat == nullptr)
+ frontend_verilog_yyerror("Found specify edge but no data spec.\n");
+
+ AstNode *cell = new AstNode(AST_CELL);
+ ast_stack.back()->children.push_back(cell);
+ cell->str = stringf("$specify$%d", autoidx++);
+ cell->children.push_back(new AstNode(AST_CELLTYPE));
+ cell->children.back()->str = target->dat ? "$specify3" : "$specify2";
+
+ char oper_polarity = 0;
+ char oper_type = oper->at(0);
+
+ if (oper->size() == 3) {
+ oper_polarity = oper->at(0);
+ oper_type = oper->at(1);
+ }
+
+ cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_type == '*', false, 1)));
+ cell->children.back()->str = "\\FULL";
+
+ cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_polarity != 0, false, 1)));
+ cell->children.back()->str = "\\SRC_DST_PEN";
+
+ cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_polarity == '+', false, 1)));
+ cell->children.back()->str = "\\SRC_DST_POL";
+
+ cell->children.push_back(new AstNode(AST_PARASET, timing->rise.t_min));
+ cell->children.back()->str = "\\T_RISE_MIN";
+
+ cell->children.push_back(new AstNode(AST_PARASET, timing->rise.t_avg));
+ cell->children.back()->str = "\\T_RISE_TYP";
+
+ cell->children.push_back(new AstNode(AST_PARASET, timing->rise.t_max));
+ cell->children.back()->str = "\\T_RISE_MAX";
+
+ cell->children.push_back(new AstNode(AST_PARASET, timing->fall.t_min));
+ cell->children.back()->str = "\\T_FALL_MIN";
+
+ cell->children.push_back(new AstNode(AST_PARASET, timing->fall.t_avg));
+ cell->children.back()->str = "\\T_FALL_TYP";
+
+ cell->children.push_back(new AstNode(AST_PARASET, timing->fall.t_max));
+ cell->children.back()->str = "\\T_FALL_MAX";
+
+ cell->children.push_back(new AstNode(AST_ARGUMENT, en_expr ? en_expr : AstNode::mkconst_int(1, false, 1)));
+ cell->children.back()->str = "\\EN";
+
+ cell->children.push_back(new AstNode(AST_ARGUMENT, src_expr));
+ cell->children.back()->str = "\\SRC";
+
+ cell->children.push_back(new AstNode(AST_ARGUMENT, target->dst));
+ cell->children.back()->str = "\\DST";
+
+ if (target->dat)
+ {
+ cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(specify_edge != 0, false, 1)));
+ cell->children.back()->str = "\\EDGE_EN";
+
+ cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(specify_edge == 'p', false, 1)));
+ cell->children.back()->str = "\\EDGE_POL";
+
+ cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(target->polarity_op != 0, false, 1)));
+ cell->children.back()->str = "\\DAT_DST_PEN";
+
+ cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(target->polarity_op == '+', false, 1)));
+ cell->children.back()->str = "\\DAT_DST_POL";
+
+ cell->children.push_back(new AstNode(AST_ARGUMENT, target->dat));
+ cell->children.back()->str = "\\DAT";
+ }
+
+ delete oper;
+ delete target;
+ delete timing;
+ } |
+ TOK_ID '(' specify_edge expr specify_condition ',' specify_edge expr specify_condition ',' expr specify_opt_arg ')' ';' {
+ if (*$1 != "$setup" && *$1 != "$hold" && *$1 != "$setuphold" && *$1 != "$removal" && *$1 != "$recovery" &&
+ *$1 != "$recrem" && *$1 != "$skew" && *$1 != "$timeskew" && *$1 != "$fullskew" && *$1 != "$nochange")
+ frontend_verilog_yyerror("Unsupported specify rule type: %s\n", $1->c_str());
+
+ AstNode *src_pen = AstNode::mkconst_int($3 != 0, false, 1);
+ AstNode *src_pol = AstNode::mkconst_int($3 == 'p', false, 1);
+ AstNode *src_expr = $4, *src_en = $5 ? $5 : AstNode::mkconst_int(1, false, 1);
+
+ AstNode *dst_pen = AstNode::mkconst_int($7 != 0, false, 1);
+ AstNode *dst_pol = AstNode::mkconst_int($7 == 'p', false, 1);
+ AstNode *dst_expr = $8, *dst_en = $9 ? $9 : AstNode::mkconst_int(1, false, 1);
+
+ AstNode *limit = $11;
+ AstNode *limit2 = $12;
+
+ AstNode *cell = new AstNode(AST_CELL);
+ ast_stack.back()->children.push_back(cell);
+ cell->str = stringf("$specify$%d", autoidx++);
+ cell->children.push_back(new AstNode(AST_CELLTYPE));
+ cell->children.back()->str = "$specrule";
+
+ cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_str(*$1)));
+ cell->children.back()->str = "\\TYPE";
+
+ cell->children.push_back(new AstNode(AST_PARASET, limit));
+ cell->children.back()->str = "\\T_LIMIT";
+
+ cell->children.push_back(new AstNode(AST_PARASET, limit2 ? limit2 : AstNode::mkconst_int(0, true)));
+ cell->children.back()->str = "\\T_LIMIT2";
+
+ cell->children.push_back(new AstNode(AST_PARASET, src_pen));
+ cell->children.back()->str = "\\SRC_PEN";
+
+ cell->children.push_back(new AstNode(AST_PARASET, src_pol));
+ cell->children.back()->str = "\\SRC_POL";
+
+ cell->children.push_back(new AstNode(AST_PARASET, dst_pen));
+ cell->children.back()->str = "\\DST_PEN";
+
+ cell->children.push_back(new AstNode(AST_PARASET, dst_pol));
+ cell->children.back()->str = "\\DST_POL";
+
+ cell->children.push_back(new AstNode(AST_ARGUMENT, src_en));
+ cell->children.back()->str = "\\SRC_EN";
+
+ cell->children.push_back(new AstNode(AST_ARGUMENT, src_expr));
+ cell->children.back()->str = "\\SRC";
+
+ cell->children.push_back(new AstNode(AST_ARGUMENT, dst_en));
+ cell->children.back()->str = "\\DST_EN";
+
+ cell->children.push_back(new AstNode(AST_ARGUMENT, dst_expr));
+ cell->children.back()->str = "\\DST";
+
+ delete $1;
+ };
+
+specify_opt_arg:
+ ',' expr {
+ $$ = $2;
+ } |
+ /* empty */ {
+ $$ = nullptr;
+ };
+
+specify_if:
+ TOK_IF '(' expr ')' {
+ $$ = $3;
+ } |
+ /* empty */ {
+ $$ = nullptr;
+ };
+
+specify_condition:
+ TOK_SPECIFY_AND expr {
+ $$ = $2;
+ } |
+ /* empty */ {
+ $$ = nullptr;
+ };
+
+specify_target:
+ expr {
+ $$ = new specify_target;
+ $$->polarity_op = 0;
+ $$->dst = $1;
+ $$->dat = nullptr;
+ } |
+ '(' expr ':' expr ')'{
+ $$ = new specify_target;
+ $$->polarity_op = 0;
+ $$->dst = $2;
+ $$->dat = $4;
+ } |
+ '(' expr TOK_NEG_INDEXED expr ')'{
+ $$ = new specify_target;
+ $$->polarity_op = '-';
+ $$->dst = $2;
+ $$->dat = $4;
+ } |
+ '(' expr TOK_POS_INDEXED expr ')'{
+ $$ = new specify_target;
+ $$->polarity_op = '+';
+ $$->dst = $2;
+ $$->dat = $4;
+ };
+
+specify_edge:
+ TOK_POSEDGE { $$ = 'p'; } |
+ TOK_NEGEDGE { $$ = 'n'; } |
+ { $$ = 0; };
+
+specify_rise_fall:
+ specify_triple {
+ $$ = new specify_rise_fall;
+ $$->rise = *$1;
+ $$->fall.t_min = $1->t_min->clone();
+ $$->fall.t_avg = $1->t_avg->clone();
+ $$->fall.t_max = $1->t_max->clone();
+ delete $1;
+ } |
+ '(' specify_triple ',' specify_triple ')' {
+ $$ = new specify_rise_fall;
+ $$->rise = *$2;
+ $$->fall = *$4;
+ delete $2;
+ delete $4;
+ };
+
+specify_triple:
+ expr {
+ $$ = new specify_triple;
+ $$->t_min = $1;
+ $$->t_avg = $1->clone();
+ $$->t_max = $1->clone();
+ } |
+ expr ':' expr ':' expr {
+ $$ = new specify_triple;
+ $$->t_min = $1;
+ $$->t_avg = $3;
+ $$->t_max = $5;
+ };
+
+/******************** ignored specify parser **************************/
+
+ignored_specify_block:
+ TOK_IGNORED_SPECIFY ignored_specify_item_opt TOK_ENDSPECIFY |
+ TOK_IGNORED_SPECIFY TOK_ENDSPECIFY ;
+
+ignored_specify_item_opt:
+ ignored_specify_item_opt ignored_specify_item |
+ ignored_specify_item ;
+
+ignored_specify_item:
specparam_declaration
// | pulsestyle_declaration
// | showcancelled_declaration
@@ -721,21 +1013,16 @@ specparam_declaration:
// and the 'non_opt_range' rule allows index ranges not allowed by 1364-2005
// exxxxtending this for SV specparam would change this anyhow
specparam_range:
- '[' constant_expression ':' constant_expression ']' ;
+ '[' ignspec_constant_expression ':' ignspec_constant_expression ']' ;
list_of_specparam_assignments:
specparam_assignment | list_of_specparam_assignments ',' specparam_assignment;
specparam_assignment:
- TOK_ID '=' constant_mintypmax_expression ;
-
-/*
-pulsestyle_declaration :
- ;
+ ignspec_id '=' constant_mintypmax_expression ;
-showcancelled_declaration :
- ;
-*/
+ignspec_opt_cond:
+ TOK_IF '(' ignspec_expr ')' | /* empty */;
path_declaration :
simple_path_declaration ';'
@@ -744,8 +1031,8 @@ path_declaration :
;
simple_path_declaration :
- parallel_path_description '=' path_delay_value |
- full_path_description '=' path_delay_value
+ ignspec_opt_cond parallel_path_description '=' path_delay_value |
+ ignspec_opt_cond full_path_description '=' path_delay_value
;
path_delay_value :
@@ -755,32 +1042,20 @@ path_delay_value :
;
list_of_path_delay_extra_expressions :
-/*
- t_path_delay_expression
- | trise_path_delay_expression ',' tfall_path_delay_expression
- | trise_path_delay_expression ',' tfall_path_delay_expression ',' tz_path_delay_expression
- | t01_path_delay_expression ',' t10_path_delay_expression ',' t0z_path_delay_expression ','
- tz1_path_delay_expression ',' t1z_path_delay_expression ',' tz0_path_delay_expression
- | t01_path_delay_expression ',' t10_path_delay_expression ',' t0z_path_delay_expression ','
- tz1_path_delay_expression ',' t1z_path_delay_expression ',' tz0_path_delay_expression ','
- t0x_path_delay_expression ',' tx1_path_delay_expression ',' t1x_path_delay_expression ','
- tx0_path_delay_expression ',' txz_path_delay_expression ',' tzx_path_delay_expression
-*/
- ',' path_delay_expression
- | ',' path_delay_expression ',' path_delay_expression
- | ',' path_delay_expression ',' path_delay_expression ','
- path_delay_expression ',' path_delay_expression ',' path_delay_expression
- | ',' path_delay_expression ',' path_delay_expression ','
- path_delay_expression ',' path_delay_expression ',' path_delay_expression ','
- path_delay_expression ',' path_delay_expression ',' path_delay_expression ','
- path_delay_expression ',' path_delay_expression ',' path_delay_expression
- ;
+ ',' path_delay_expression | ',' path_delay_expression list_of_path_delay_extra_expressions;
+
+specify_edge_identifier :
+ TOK_POSEDGE | TOK_NEGEDGE ;
parallel_path_description :
- '(' specify_input_terminal_descriptor opt_polarity_operator '=' '>' specify_output_terminal_descriptor ')' ;
+ '(' specify_input_terminal_descriptor opt_polarity_operator '=' '>' specify_output_terminal_descriptor ')' |
+ '(' specify_edge_identifier specify_input_terminal_descriptor '=' '>' '(' specify_output_terminal_descriptor opt_polarity_operator ':' ignspec_expr ')' ')' |
+ '(' specify_edge_identifier specify_input_terminal_descriptor '=' '>' '(' specify_output_terminal_descriptor TOK_POS_INDEXED ignspec_expr ')' ')' ;
full_path_description :
- '(' list_of_path_inputs '*' '>' list_of_path_outputs ')' ;
+ '(' list_of_path_inputs '*' '>' list_of_path_outputs ')' |
+ '(' specify_edge_identifier list_of_path_inputs '*' '>' '(' list_of_path_outputs opt_polarity_operator ':' ignspec_expr ')' ')' |
+ '(' specify_edge_identifier list_of_path_inputs '*' '>' '(' list_of_path_outputs TOK_POS_INDEXED ignspec_expr ')' ')' ;
// This was broken into 2 rules to solve shift/reduce conflicts
list_of_path_inputs :
@@ -802,88 +1077,46 @@ opt_polarity_operator :
// Good enough for the time being
specify_input_terminal_descriptor :
- TOK_ID ;
+ ignspec_id ;
// Good enough for the time being
specify_output_terminal_descriptor :
- TOK_ID ;
+ ignspec_id ;
system_timing_declaration :
- TOK_ID '(' system_timing_args ')' ';' ;
+ ignspec_id '(' system_timing_args ')' ';' ;
system_timing_arg :
- TOK_POSEDGE TOK_ID |
- TOK_NEGEDGE TOK_ID |
- expr ;
+ TOK_POSEDGE ignspec_id |
+ TOK_NEGEDGE ignspec_id |
+ ignspec_expr ;
system_timing_args :
system_timing_arg |
system_timing_args ',' system_timing_arg ;
-/*
-t_path_delay_expression :
- path_delay_expression;
-
-trise_path_delay_expression :
- path_delay_expression;
-
-tfall_path_delay_expression :
- path_delay_expression;
-
-tz_path_delay_expression :
- path_delay_expression;
-
-t01_path_delay_expression :
- path_delay_expression;
-
-t10_path_delay_expression :
- path_delay_expression;
-
-t0z_path_delay_expression :
- path_delay_expression;
-
-tz1_path_delay_expression :
- path_delay_expression;
-
-t1z_path_delay_expression :
- path_delay_expression;
-
-tz0_path_delay_expression :
- path_delay_expression;
-
-t0x_path_delay_expression :
- path_delay_expression;
-
-tx1_path_delay_expression :
- path_delay_expression;
-
-t1x_path_delay_expression :
- path_delay_expression;
-
-tx0_path_delay_expression :
- path_delay_expression;
-
-txz_path_delay_expression :
- path_delay_expression;
-
-tzx_path_delay_expression :
- path_delay_expression;
-*/
-
path_delay_expression :
- constant_expression;
+ ignspec_constant_expression;
constant_mintypmax_expression :
- constant_expression
- | constant_expression ':' constant_expression ':' constant_expression
+ ignspec_constant_expression
+ | ignspec_constant_expression ':' ignspec_constant_expression ':' ignspec_constant_expression
;
// for the time being this is OK, but we may write our own expr here.
// as I'm not sure it is legal to use a full expr here (probably not)
// On the other hand, other rules requiring constant expressions also use 'expr'
// (such as param assignment), so we may leave this as-is, perhaps adding runtime checks for constant-ness
-constant_expression:
- expr ;
+ignspec_constant_expression:
+ expr { delete $1; };
+
+ignspec_expr:
+ expr { delete $1; };
+
+ignspec_id:
+ TOK_ID { delete $1; };
+
+/**********************************************************************/
param_signed:
TOK_SIGNED {
@@ -917,17 +1150,19 @@ param_range:
};
param_decl:
- TOK_PARAMETER {
+ attr TOK_PARAMETER {
astbuf1 = new AstNode(AST_PARAMETER);
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
+ append_attr(astbuf1, $1);
} param_signed param_integer param_real param_range param_decl_list ';' {
delete astbuf1;
};
localparam_decl:
- TOK_LOCALPARAM {
+ attr TOK_LOCALPARAM {
astbuf1 = new AstNode(AST_LOCALPARAM);
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
+ append_attr(astbuf1, $1);
} param_signed param_integer param_real param_range param_decl_list ';' {
delete astbuf1;
};
@@ -1069,7 +1304,12 @@ wire_name_and_opt_assign:
wire_name '=' expr {
AstNode *wire = new AstNode(AST_IDENTIFIER);
wire->str = ast_stack.back()->children.back()->str;
- if (astbuf1->is_reg)
+ if (astbuf1->is_input) {
+ if (astbuf1->attributes.count("\\defaultvalue"))
+ delete astbuf1->attributes.at("\\defaultvalue");
+ astbuf1->attributes["\\defaultvalue"] = $3;
+ } else
+ if (astbuf1->is_reg || astbuf1->is_logic)
ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $3))));
else
ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $3));
@@ -1094,7 +1334,13 @@ wire_name:
node->children.push_back(rng);
}
node->type = AST_MEMORY;
- node->children.push_back($2);
+ auto *rangeNode = $2;
+ if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) {
+ // SV array size [n], rewrite as [n-1:0]
+ rangeNode->children[0] = new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true));
+ rangeNode->children.push_back(AstNode::mkconst_int(0, false));
+ }
+ node->children.push_back(rangeNode);
}
if (current_function_or_task == NULL) {
if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) {
@@ -1241,27 +1487,40 @@ cell_port_list_rules:
cell_port | cell_port_list_rules ',' cell_port;
cell_port:
- /* empty */ {
+ attr {
AstNode *node = new AstNode(AST_ARGUMENT);
astbuf2->children.push_back(node);
+ free_attr($1);
} |
- expr {
+ attr expr {
AstNode *node = new AstNode(AST_ARGUMENT);
astbuf2->children.push_back(node);
- node->children.push_back($1);
+ node->children.push_back($2);
+ free_attr($1);
} |
- '.' TOK_ID '(' expr ')' {
+ attr '.' TOK_ID '(' expr ')' {
AstNode *node = new AstNode(AST_ARGUMENT);
- node->str = *$2;
+ node->str = *$3;
astbuf2->children.push_back(node);
- node->children.push_back($4);
- delete $2;
+ node->children.push_back($5);
+ delete $3;
+ free_attr($1);
} |
- '.' TOK_ID '(' ')' {
+ attr '.' TOK_ID '(' ')' {
AstNode *node = new AstNode(AST_ARGUMENT);
- node->str = *$2;
+ node->str = *$3;
astbuf2->children.push_back(node);
- delete $2;
+ delete $3;
+ free_attr($1);
+ } |
+ attr '.' TOK_ID {
+ AstNode *node = new AstNode(AST_ARGUMENT);
+ node->str = *$3;
+ astbuf2->children.push_back(node);
+ node->children.push_back(new AstNode(AST_IDENTIFIER));
+ node->children.back()->str = *$3;
+ delete $3;
+ free_attr($1);
};
always_stmt:
@@ -1341,6 +1600,9 @@ opt_property:
TOK_PROPERTY {
$$ = true;
} |
+ TOK_FINAL {
+ $$ = false;
+ } |
/* empty */ {
$$ = false;
};
@@ -1574,6 +1836,16 @@ behavioral_stmt:
} opt_arg_list ';'{
ast_stack.pop_back();
} |
+ TOK_MSG_TASKS attr {
+ AstNode *node = new AstNode(AST_TCALL);
+ node->str = *$1;
+ delete $1;
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ append_attr(node, $2);
+ } opt_arg_list ';'{
+ ast_stack.pop_back();
+ } |
attr TOK_BEGIN opt_label {
AstNode *node = new AstNode(AST_BLOCK);
ast_stack.back()->children.push_back(node);
@@ -1869,6 +2141,15 @@ gen_stmt:
if ($6 != NULL)
delete $6;
ast_stack.pop_back();
+ } |
+ TOK_MSG_TASKS {
+ AstNode *node = new AstNode(AST_TECALL);
+ node->str = *$1;
+ delete $1;
+ ast_stack.back()->children.push_back(node);
+ ast_stack.push_back(node);
+ } opt_arg_list ';'{
+ ast_stack.pop_back();
};
gen_stmt_block:
@@ -2139,4 +2420,3 @@ concat_list:
$$ = $3;
$$->children.push_back($1);
};
-
diff --git a/kernel/cellaigs.cc b/kernel/cellaigs.cc
index 5fd76afe..26c625f8 100644
--- a/kernel/cellaigs.cc
+++ b/kernel/cellaigs.cc
@@ -453,7 +453,7 @@ Aig::Aig(Cell *cell)
int B = mk.inport("\\B");
int C = mk.inport("\\C");
int D = mk.inport("\\D");
- int Y = mk.nand_gate(mk.nor_gate(A, B), mk.nor_gate(C, D));
+ int Y = mk.nand_gate(mk.or_gate(A, B), mk.or_gate(C, D));
mk.outport(Y, "\\Y");
goto optimize;
}
diff --git a/kernel/celltypes.h b/kernel/celltypes.h
index ae88f4aa..4e91eddd 100644
--- a/kernel/celltypes.h
+++ b/kernel/celltypes.h
@@ -85,6 +85,8 @@ struct CellTypes
setup_internals_eval();
IdString A = "\\A", B = "\\B", EN = "\\EN", Y = "\\Y";
+ IdString SRC = "\\SRC", DST = "\\DST", DAT = "\\DAT";
+ IdString EN_SRC = "\\EN_SRC", EN_DST = "\\EN_DST";
setup_type("$tribuf", {A, EN}, {Y}, true);
@@ -99,6 +101,9 @@ struct CellTypes
setup_type("$allconst", pool<RTLIL::IdString>(), {Y}, true);
setup_type("$allseq", pool<RTLIL::IdString>(), {Y}, true);
setup_type("$equiv", {A, B}, {Y}, true);
+ setup_type("$specify2", {EN, SRC, DST}, pool<RTLIL::IdString>(), true);
+ setup_type("$specify3", {EN, SRC, DST, DAT}, pool<RTLIL::IdString>(), true);
+ setup_type("$specrule", {EN_SRC, EN_DST, SRC, DST}, pool<RTLIL::IdString>(), true);
}
void setup_internals_eval()
@@ -464,7 +469,7 @@ struct CellTypes
if (cell->type == "$_AOI4_")
return eval_not(const_or(const_and(arg1, arg2, false, false, 1), const_and(arg3, arg4, false, false, 1), false, false, 1));
if (cell->type == "$_OAI4_")
- return eval_not(const_and(const_or(arg1, arg2, false, false, 1), const_and(arg3, arg4, false, false, 1), false, false, 1));
+ return eval_not(const_and(const_or(arg1, arg2, false, false, 1), const_or(arg3, arg4, false, false, 1), false, false, 1));
log_assert(arg4.bits.size() == 0);
return eval(cell, arg1, arg2, arg3, errp);
diff --git a/kernel/cost.h b/kernel/cost.h
index e795b571..41a09eb6 100644
--- a/kernel/cost.h
+++ b/kernel/cost.h
@@ -26,7 +26,7 @@ YOSYS_NAMESPACE_BEGIN
int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache = nullptr);
-int get_cell_cost(RTLIL::IdString type, const dict<RTLIL::IdString, RTLIL::Const> &parameters = dict<RTLIL::IdString, RTLIL::Const>(),
+inline int get_cell_cost(RTLIL::IdString type, const dict<RTLIL::IdString, RTLIL::Const> &parameters = dict<RTLIL::IdString, RTLIL::Const>(),
RTLIL::Design *design = nullptr, dict<RTLIL::IdString, int> *mod_cost_cache = nullptr)
{
static dict<RTLIL::IdString, int> gate_cost = {
@@ -76,7 +76,7 @@ int get_cell_cost(RTLIL::IdString type, const dict<RTLIL::IdString, RTLIL::Const
return 1;
}
-int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache)
+inline int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache)
{
return get_cell_cost(cell->type, cell->parameters, cell->module->design, mod_cost_cache);
}
diff --git a/kernel/driver.cc b/kernel/driver.cc
index a0bb7e60..f273057d 100644
--- a/kernel/driver.cc
+++ b/kernel/driver.cc
@@ -110,6 +110,10 @@ int main(int argc, char **argv)
log_error_stderr = true;
yosys_banner();
yosys_setup();
+#ifdef WITH_PYTHON
+ PyRun_SimpleString(("sys.path.append(\""+proc_self_dirname()+"\")").c_str());
+ PyRun_SimpleString(("sys.path.append(\""+proc_share_dirname()+"plugins\")").c_str());
+#endif
if (argc == 2)
{
@@ -291,6 +295,9 @@ int main(int argc, char **argv)
printf(" -E <depsfile>\n");
printf(" write a Makefile dependencies file with in- and output file names\n");
printf("\n");
+ printf(" -g\n");
+ printf(" globally enable debug log messages\n");
+ printf("\n");
printf(" -V\n");
printf(" print version information and exit\n");
printf("\n");
@@ -311,7 +318,7 @@ int main(int argc, char **argv)
}
int opt;
- while ((opt = getopt(argc, argv, "MXAQTVSm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:")) != -1)
+ while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:")) != -1)
{
switch (opt)
{
@@ -336,6 +343,9 @@ int main(int argc, char **argv)
case 'S':
passes_commands.push_back("synth");
break;
+ case 'g':
+ log_force_debug++;
+ break;
case 'm':
plugin_filenames.push_back(optarg);
break;
@@ -469,6 +479,10 @@ int main(int argc, char **argv)
#endif
yosys_setup();
+#ifdef WITH_PYTHON
+ PyRun_SimpleString(("sys.path.append(\""+proc_self_dirname()+"\")").c_str());
+ PyRun_SimpleString(("sys.path.append(\""+proc_share_dirname()+"plugins\")").c_str());
+#endif
log_error_atexit = yosys_atexit;
for (auto &fn : plugin_filenames)
@@ -515,13 +529,13 @@ int main(int argc, char **argv)
log_error("Can't open dependencies file for writing: %s\n", strerror(errno));
bool first = true;
for (auto fn : yosys_output_files) {
- fprintf(f, "%s%s", first ? "" : " ", fn.c_str());
+ fprintf(f, "%s%s", first ? "" : " ", escape_filename_spaces(fn).c_str());
first = false;
}
fprintf(f, ":");
for (auto fn : yosys_input_files) {
if (yosys_output_files.count(fn) == 0)
- fprintf(f, " %s", fn.c_str());
+ fprintf(f, " %s", escape_filename_spaces(fn).c_str());
}
fprintf(f, "\n");
}
diff --git a/kernel/log.cc b/kernel/log.cc
index 400a549d..a7820950 100644
--- a/kernel/log.cc
+++ b/kernel/log.cc
@@ -56,6 +56,10 @@ int log_verbose_level;
string log_last_error;
void (*log_error_atexit)() = NULL;
+int log_make_debug = 0;
+int log_force_debug = 0;
+int log_debug_suppressed = 0;
+
vector<int> header_count;
pool<RTLIL::IdString> log_id_cache;
vector<shared_str> string_buf;
@@ -92,6 +96,9 @@ void logv(const char *format, va_list ap)
format++;
}
+ if (log_make_debug && !ys_debug(1))
+ return;
+
std::string str = vstringf(format, ap);
if (str.empty())
@@ -223,6 +230,9 @@ static void logv_warning_with_prefix(const char *prefix,
}
else
{
+ int bak_log_make_debug = log_make_debug;
+ log_make_debug = 0;
+
for (auto &re : log_werror_regexes)
if (std::regex_search(message, re))
log_error("%s", message.c_str());
@@ -247,6 +257,7 @@ static void logv_warning_with_prefix(const char *prefix,
}
log_warnings_count++;
+ log_make_debug = bak_log_make_debug;
}
}
@@ -266,11 +277,22 @@ void log_file_warning(const std::string &filename, int lineno,
va_list ap;
va_start(ap, format);
std::string prefix = stringf("%s:%d: Warning: ",
- filename.c_str(), lineno);
+ filename.c_str(), lineno);
logv_warning_with_prefix(prefix.c_str(), format, ap);
va_end(ap);
}
+void log_file_info(const std::string &filename, int lineno,
+ const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ std::string fmt = stringf("%s:%d: Info: %s",
+ filename.c_str(), lineno, format);
+ logv(fmt.c_str(), ap);
+ va_end(ap);
+}
+
YS_ATTRIBUTE(noreturn)
static void logv_error_with_prefix(const char *prefix,
const char *format, va_list ap)
@@ -278,6 +300,9 @@ static void logv_error_with_prefix(const char *prefix,
#ifdef EMSCRIPTEN
auto backup_log_files = log_files;
#endif
+ int bak_log_make_debug = log_make_debug;
+ log_make_debug = 0;
+ log_suppressed();
if (log_errfile != NULL)
log_files.push_back(log_errfile);
@@ -291,6 +316,8 @@ static void logv_error_with_prefix(const char *prefix,
log("%s%s", prefix, log_last_error.c_str());
log_flush();
+ log_make_debug = bak_log_make_debug;
+
if (log_error_atexit)
log_error_atexit();
diff --git a/kernel/log.h b/kernel/log.h
index 75993902..3e1facae 100644
--- a/kernel/log.h
+++ b/kernel/log.h
@@ -64,6 +64,10 @@ extern int log_verbose_level;
extern string log_last_error;
extern void (*log_error_atexit)();
+extern int log_make_debug;
+extern int log_force_debug;
+extern int log_debug_suppressed;
+
void logv(const char *format, va_list ap);
void logv_header(RTLIL::Design *design, const char *format, va_list ap);
void logv_warning(const char *format, va_list ap);
@@ -76,12 +80,52 @@ void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
// Log with filename to report a problem in a source file.
void log_file_warning(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4));
+void log_file_info(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4));
void log_warning_noprefix(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
YS_NORETURN void log_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2), noreturn);
void log_file_error(const string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4), noreturn);
YS_NORETURN void log_cmd_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2), noreturn);
+#ifndef NDEBUG
+static inline bool ys_debug(int n = 0) { if (log_force_debug) return true; log_debug_suppressed += n; return false; }
+# define log_debug(...) do { if (ys_debug(1)) log(__VA_ARGS__); } while (0)
+#else
+static inline bool ys_debug(int n = 0) { return false; }
+# define log_debug(_fmt, ...) do { } while (0)
+#endif
+
+static inline void log_suppressed() {
+ if (log_debug_suppressed && !log_make_debug) {
+ log("<suppressed ~%d debug messages>\n", log_debug_suppressed);
+ log_debug_suppressed = 0;
+ }
+}
+
+struct LogMakeDebugHdl {
+ bool status = false;
+ LogMakeDebugHdl(bool start_on = false) {
+ if (start_on)
+ on();
+ }
+ ~LogMakeDebugHdl() {
+ off();
+ }
+ void on() {
+ if (status) return;
+ status=true;
+ log_make_debug++;
+ }
+ void off_silent() {
+ if (!status) return;
+ status=false;
+ log_make_debug--;
+ }
+ void off() {
+ off_silent();
+ }
+};
+
void log_spacer();
void log_push();
void log_pop();
diff --git a/kernel/register.cc b/kernel/register.cc
index 64956401..26da96b9 100644
--- a/kernel/register.cc
+++ b/kernel/register.cc
@@ -87,6 +87,7 @@ Pass::pre_post_exec_state_t Pass::pre_execute()
void Pass::post_execute(Pass::pre_post_exec_state_t state)
{
IdString::checkpoint();
+ log_suppressed();
int64_t time_ns = PerformanceTimer::query() - state.begin_ns;
runtime_ns += time_ns;
@@ -544,6 +545,7 @@ void Backend::extra_args(std::ostream *&f, std::string &filename, std::vector<st
}
filename = arg;
+ rewrite_filename(filename);
std::ofstream *ff = new std::ofstream;
ff->open(filename.c_str(), std::ofstream::trunc);
yosys_output_files.insert(filename);
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
index b3214579..a09f4a0d 100644
--- a/kernel/rtlil.cc
+++ b/kernel/rtlil.cc
@@ -76,6 +76,13 @@ RTLIL::Const::Const(const std::vector<bool> &bits)
this->bits.push_back(b ? RTLIL::S1 : RTLIL::S0);
}
+RTLIL::Const::Const(const RTLIL::Const &c)
+{
+ flags = c.flags;
+ for (auto b : c.bits)
+ this->bits.push_back(b);
+}
+
bool RTLIL::Const::operator <(const RTLIL::Const &other) const
{
if (bits.size() != other.bits.size())
@@ -207,16 +214,23 @@ bool RTLIL::Const::is_fully_undef() const
return true;
}
-void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id)
+void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id, bool value)
{
- attributes[id] = RTLIL::Const(1);
+ if (value)
+ attributes[id] = RTLIL::Const(1);
+ else {
+ const auto it = attributes.find(id);
+ if (it != attributes.end())
+ attributes.erase(it);
+ }
}
bool RTLIL::AttrObject::get_bool_attribute(RTLIL::IdString id) const
{
- if (attributes.count(id) == 0)
+ const auto it = attributes.find(id);
+ if (it == attributes.end())
return false;
- return attributes.at(id).as_bool();
+ return it->second.as_bool();
}
void RTLIL::AttrObject::set_strpool_attribute(RTLIL::IdString id, const pool<string> &data)
@@ -360,6 +374,10 @@ RTLIL::Design::Design()
refcount_modules_ = 0;
selection_stack.push_back(RTLIL::Selection());
+
+#ifdef WITH_PYTHON
+ RTLIL::Design::get_all_designs()->insert(std::pair<unsigned int, RTLIL::Design*>(hashidx_, this));
+#endif
}
RTLIL::Design::~Design()
@@ -370,7 +388,18 @@ RTLIL::Design::~Design()
delete n;
for (auto n : verilog_globals)
delete n;
+#ifdef WITH_PYTHON
+ RTLIL::Design::get_all_designs()->erase(hashidx_);
+#endif
+}
+
+#ifdef WITH_PYTHON
+static std::map<unsigned int, RTLIL::Design*> all_designs;
+std::map<unsigned int, RTLIL::Design*> *RTLIL::Design::get_all_designs(void)
+{
+ return &all_designs;
}
+#endif
RTLIL::ObjRange<RTLIL::Module*> RTLIL::Design::modules()
{
@@ -589,7 +618,7 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_modules() const
std::vector<RTLIL::Module*> result;
result.reserve(modules_.size());
for (auto &it : modules_)
- if (selected_module(it.first) && !it.second->get_bool_attribute("\\blackbox"))
+ if (selected_module(it.first) && !it.second->get_blackbox_attribute())
result.push_back(it.second);
return result;
}
@@ -599,7 +628,7 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_whole_modules() const
std::vector<RTLIL::Module*> result;
result.reserve(modules_.size());
for (auto &it : modules_)
- if (selected_whole_module(it.first) && !it.second->get_bool_attribute("\\blackbox"))
+ if (selected_whole_module(it.first) && !it.second->get_blackbox_attribute())
result.push_back(it.second);
return result;
}
@@ -609,7 +638,7 @@ std::vector<RTLIL::Module*> RTLIL::Design::selected_whole_modules_warn() const
std::vector<RTLIL::Module*> result;
result.reserve(modules_.size());
for (auto &it : modules_)
- if (it.second->get_bool_attribute("\\blackbox"))
+ if (it.second->get_blackbox_attribute())
continue;
else if (selected_whole_module(it.first))
result.push_back(it.second);
@@ -627,6 +656,10 @@ RTLIL::Module::Module()
design = nullptr;
refcount_wires_ = 0;
refcount_cells_ = 0;
+
+#ifdef WITH_PYTHON
+ RTLIL::Module::get_all_modules()->insert(std::pair<unsigned int, RTLIL::Module*>(hashidx_, this));
+#endif
}
RTLIL::Module::~Module()
@@ -639,6 +672,41 @@ RTLIL::Module::~Module()
delete it->second;
for (auto it = processes.begin(); it != processes.end(); ++it)
delete it->second;
+#ifdef WITH_PYTHON
+ RTLIL::Module::get_all_modules()->erase(hashidx_);
+#endif
+}
+
+#ifdef WITH_PYTHON
+static std::map<unsigned int, RTLIL::Module*> all_modules;
+std::map<unsigned int, RTLIL::Module*> *RTLIL::Module::get_all_modules(void)
+{
+ return &all_modules;
+}
+#endif
+
+void RTLIL::Module::makeblackbox()
+{
+ pool<RTLIL::Wire*> delwires;
+
+ for (auto it = wires_.begin(); it != wires_.end(); ++it)
+ if (!it->second->port_input && !it->second->port_output)
+ delwires.insert(it->second);
+
+ for (auto it = memories.begin(); it != memories.end(); ++it)
+ delete it->second;
+ memories.clear();
+
+ for (auto it = cells_.begin(); it != cells_.end(); ++it)
+ delete it->second;
+ cells_.clear();
+
+ for (auto it = processes.begin(); it != processes.end(); ++it)
+ delete it->second;
+ processes.clear();
+
+ remove(delwires);
+ set_bool_attribute("\\blackbox");
}
void RTLIL::Module::reprocess_module(RTLIL::Design *, dict<RTLIL::IdString, RTLIL::Module *>)
@@ -1130,6 +1198,46 @@ namespace {
return;
}
+ if (cell->type.in("$specify2", "$specify3")) {
+ param_bool("\\FULL");
+ param_bool("\\SRC_DST_PEN");
+ param_bool("\\SRC_DST_POL");
+ param("\\T_RISE_MIN");
+ param("\\T_RISE_TYP");
+ param("\\T_RISE_MAX");
+ param("\\T_FALL_MIN");
+ param("\\T_FALL_TYP");
+ param("\\T_FALL_MAX");
+ port("\\EN", 1);
+ port("\\SRC", param("\\SRC_WIDTH"));
+ port("\\DST", param("\\DST_WIDTH"));
+ if (cell->type == "$specify3") {
+ param_bool("\\EDGE_EN");
+ param_bool("\\EDGE_POL");
+ param_bool("\\DAT_DST_PEN");
+ param_bool("\\DAT_DST_POL");
+ port("\\DAT", param("\\DST_WIDTH"));
+ }
+ check_expected();
+ return;
+ }
+
+ if (cell->type == "$specrule") {
+ param("\\TYPE");
+ param_bool("\\SRC_PEN");
+ param_bool("\\SRC_POL");
+ param_bool("\\DST_PEN");
+ param_bool("\\DST_POL");
+ param("\\T_LIMIT");
+ param("\\T_LIMIT2");
+ port("\\SRC_EN", 1);
+ port("\\DST_EN", 1);
+ port("\\SRC", param("\\SRC_WIDTH"));
+ port("\\DST", param("\\DST_WIDTH"));
+ check_expected();
+ return;
+ }
+
if (cell->type == "$_BUF_") { check_gate("AY"); return; }
if (cell->type == "$_NOT_") { check_gate("AY"); return; }
if (cell->type == "$_AND_") { check_gate("ABY"); return; }
@@ -1273,7 +1381,34 @@ void RTLIL::Module::check()
for (auto &it : processes) {
log_assert(it.first == it.second->name);
log_assert(!it.first.empty());
- // FIXME: More checks here..
+ log_assert(it.second->root_case.compare.empty());
+ std::vector<CaseRule*> all_cases = {&it.second->root_case};
+ for (size_t i = 0; i < all_cases.size(); i++) {
+ for (auto &switch_it : all_cases[i]->switches) {
+ for (auto &case_it : switch_it->cases) {
+ for (auto &compare_it : case_it->compare) {
+ log_assert(switch_it->signal.size() == compare_it.size());
+ }
+ all_cases.push_back(case_it);
+ }
+ }
+ }
+ for (auto &sync_it : it.second->syncs) {
+ switch (sync_it->type) {
+ case SyncType::ST0:
+ case SyncType::ST1:
+ case SyncType::STp:
+ case SyncType::STn:
+ case SyncType::STe:
+ log_assert(!sync_it->signal.empty());
+ break;
+ case SyncType::STa:
+ case SyncType::STg:
+ case SyncType::STi:
+ log_assert(sync_it->signal.empty());
+ break;
+ }
+ }
}
for (auto &it : connections_) {
@@ -1406,7 +1541,10 @@ void RTLIL::Module::add(RTLIL::Cell *cell)
cell->module = this;
}
-namespace {
+void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires)
+{
+ log_assert(refcount_wires_ == 0);
+
struct DeleteWireWorker
{
RTLIL::Module *module;
@@ -1421,17 +1559,29 @@ namespace {
}
sig = chunks;
}
- };
-}
-void RTLIL::Module::remove(const pool<RTLIL::Wire*> &wires)
-{
- log_assert(refcount_wires_ == 0);
+ void operator()(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) {
+ log_assert(GetSize(lhs) == GetSize(rhs));
+ RTLIL::SigSpec new_lhs, new_rhs;
+ for (int i = 0; i < GetSize(lhs); i++) {
+ RTLIL::SigBit lhs_bit = lhs[i];
+ if (lhs_bit.wire != nullptr && wires_p->count(lhs_bit.wire))
+ continue;
+ RTLIL::SigBit rhs_bit = rhs[i];
+ if (rhs_bit.wire != nullptr && wires_p->count(rhs_bit.wire))
+ continue;
+ new_lhs.append(lhs_bit);
+ new_rhs.append(rhs_bit);
+ }
+ lhs = new_lhs;
+ rhs = new_rhs;
+ }
+ };
DeleteWireWorker delete_wire_worker;
delete_wire_worker.module = this;
delete_wire_worker.wires_p = &wires;
- rewrite_sigspecs(delete_wire_worker);
+ rewrite_sigspecs2(delete_wire_worker);
for (auto &it : wires) {
log_assert(wires_.count(it->name) != 0);
@@ -2202,8 +2352,27 @@ RTLIL::Wire::Wire()
port_input = false;
port_output = false;
upto = false;
+
+#ifdef WITH_PYTHON
+ RTLIL::Wire::get_all_wires()->insert(std::pair<unsigned int, RTLIL::Wire*>(hashidx_, this));
+#endif
}
+RTLIL::Wire::~Wire()
+{
+#ifdef WITH_PYTHON
+ RTLIL::Wire::get_all_wires()->erase(hashidx_);
+#endif
+}
+
+#ifdef WITH_PYTHON
+static std::map<unsigned int, RTLIL::Wire*> all_wires;
+std::map<unsigned int, RTLIL::Wire*> *RTLIL::Wire::get_all_wires(void)
+{
+ return &all_wires;
+}
+#endif
+
RTLIL::Memory::Memory()
{
static unsigned int hashidx_count = 123456789;
@@ -2213,6 +2382,9 @@ RTLIL::Memory::Memory()
width = 1;
start_offset = 0;
size = 0;
+#ifdef WITH_PYTHON
+ RTLIL::Memory::get_all_memorys()->insert(std::pair<unsigned int, RTLIL::Memory*>(hashidx_, this));
+#endif
}
RTLIL::Cell::Cell() : module(nullptr)
@@ -2223,8 +2395,27 @@ RTLIL::Cell::Cell() : module(nullptr)
// log("#memtrace# %p\n", this);
memhasher();
+
+#ifdef WITH_PYTHON
+ RTLIL::Cell::get_all_cells()->insert(std::pair<unsigned int, RTLIL::Cell*>(hashidx_, this));
+#endif
+}
+
+RTLIL::Cell::~Cell()
+{
+#ifdef WITH_PYTHON
+ RTLIL::Cell::get_all_cells()->erase(hashidx_);
+#endif
}
+#ifdef WITH_PYTHON
+static std::map<unsigned int, RTLIL::Cell*> all_cells;
+std::map<unsigned int, RTLIL::Cell*> *RTLIL::Cell::get_all_cells(void)
+{
+ return &all_cells;
+}
+#endif
+
bool RTLIL::Cell::hasPort(RTLIL::IdString portname) const
{
return connections_.count(portname) != 0;
@@ -2484,6 +2675,14 @@ RTLIL::SigChunk::SigChunk(RTLIL::SigBit bit)
width = 1;
}
+RTLIL::SigChunk::SigChunk(const RTLIL::SigChunk &sigchunk) : data(sigchunk.data)
+{
+ wire = sigchunk.wire;
+ data = sigchunk.data;
+ width = sigchunk.width;
+ offset = sigchunk.offset;
+}
+
RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const
{
RTLIL::SigChunk ret;
@@ -3343,7 +3542,7 @@ bool RTLIL::SigSpec::operator ==(const RTLIL::SigSpec &other) const
pack();
other.pack();
- if (chunks_.size() != chunks_.size())
+ if (chunks_.size() != other.chunks_.size())
return false;
updhash();
@@ -3868,5 +4067,15 @@ RTLIL::Process *RTLIL::Process::clone() const
return new_proc;
}
+#ifdef WITH_PYTHON
+RTLIL::Memory::~Memory()
+{
+ RTLIL::Memory::get_all_memorys()->erase(hashidx_);
+}
+static std::map<unsigned int, RTLIL::Memory*> all_memorys;
+std::map<unsigned int, RTLIL::Memory*> *RTLIL::Memory::get_all_memorys(void)
+{
+ return &all_memorys;
+}
+#endif
YOSYS_NAMESPACE_END
-
diff --git a/kernel/rtlil.h b/kernel/rtlil.h
index 52496e70..7b7367fb 100644
--- a/kernel/rtlil.h
+++ b/kernel/rtlil.h
@@ -50,7 +50,7 @@ namespace RTLIL
CONST_FLAG_NONE = 0,
CONST_FLAG_STRING = 1,
CONST_FLAG_SIGNED = 2, // only used for parameters
- CONST_FLAG_REAL = 4 // unused -- to be used for parameters
+ CONST_FLAG_REAL = 4 // only used for parameters
};
struct Const;
@@ -517,6 +517,8 @@ struct RTLIL::Const
Const(RTLIL::State bit, int width = 1);
Const(const std::vector<RTLIL::State> &bits) : bits(bits) { flags = CONST_FLAG_NONE; }
Const(const std::vector<bool> &bits);
+ Const(const RTLIL::Const &c);
+ RTLIL::Const &operator =(const RTLIL::Const &other) = default;
bool operator <(const RTLIL::Const &other) const;
bool operator ==(const RTLIL::Const &other) const;
@@ -566,9 +568,13 @@ struct RTLIL::AttrObject
{
dict<RTLIL::IdString, RTLIL::Const> attributes;
- void set_bool_attribute(RTLIL::IdString id);
+ void set_bool_attribute(RTLIL::IdString id, bool value=true);
bool get_bool_attribute(RTLIL::IdString id) const;
+ bool get_blackbox_attribute(bool ignore_wb=false) const {
+ return get_bool_attribute("\\blackbox") || (!ignore_wb && get_bool_attribute("\\whitebox"));
+ }
+
void set_strpool_attribute(RTLIL::IdString id, const pool<string> &data);
void add_strpool_attribute(RTLIL::IdString id, const pool<string> &data);
pool<string> get_strpool_attribute(RTLIL::IdString id) const;
@@ -591,8 +597,11 @@ struct RTLIL::SigChunk
SigChunk(int val, int width = 32);
SigChunk(RTLIL::State bit, int width = 1);
SigChunk(RTLIL::SigBit bit);
+ SigChunk(const RTLIL::SigChunk &sigchunk);
+ RTLIL::SigChunk &operator =(const RTLIL::SigChunk &other) = default;
RTLIL::SigChunk extract(int offset, int length) const;
+ inline int size() const { return width; }
bool operator <(const RTLIL::SigChunk &other) const;
bool operator ==(const RTLIL::SigChunk &other) const;
@@ -615,6 +624,8 @@ struct RTLIL::SigBit
SigBit(const RTLIL::SigChunk &chunk);
SigBit(const RTLIL::SigChunk &chunk, int index);
SigBit(const RTLIL::SigSpec &sig);
+ SigBit(const RTLIL::SigBit &sigbit);
+ RTLIL::SigBit &operator =(const RTLIL::SigBit &other) = default;
bool operator <(const RTLIL::SigBit &other) const;
bool operator ==(const RTLIL::SigBit &other) const;
@@ -936,9 +947,13 @@ struct RTLIL::Design
}
}
+
std::vector<RTLIL::Module*> selected_modules() const;
std::vector<RTLIL::Module*> selected_whole_modules() const;
std::vector<RTLIL::Module*> selected_whole_modules_warn() const;
+#ifdef WITH_PYTHON
+ static std::map<unsigned int, RTLIL::Design*> *get_all_designs(void);
+#endif
};
struct RTLIL::Module : public RTLIL::AttrObject
@@ -976,6 +991,7 @@ public:
virtual void sort();
virtual void check();
virtual void optimize();
+ virtual void makeblackbox();
void connect(const RTLIL::SigSig &conn);
void connect(const RTLIL::SigSpec &lhs, const RTLIL::SigSpec &rhs);
@@ -986,6 +1002,7 @@ public:
void fixup_ports();
template<typename T> void rewrite_sigspecs(T &functor);
+ template<typename T> void rewrite_sigspecs2(T &functor);
void cloneInto(RTLIL::Module *new_mod) const;
virtual RTLIL::Module *clone() const;
@@ -1194,6 +1211,10 @@ public:
RTLIL::SigSpec Allconst (RTLIL::IdString name, int width = 1, const std::string &src = "");
RTLIL::SigSpec Allseq (RTLIL::IdString name, int width = 1, const std::string &src = "");
RTLIL::SigSpec Initstate (RTLIL::IdString name, const std::string &src = "");
+
+#ifdef WITH_PYTHON
+ static std::map<unsigned int, RTLIL::Module*> *get_all_modules(void);
+#endif
};
struct RTLIL::Wire : public RTLIL::AttrObject
@@ -1205,7 +1226,7 @@ protected:
// use module->addWire() and module->remove() to create or destroy wires
friend struct RTLIL::Module;
Wire();
- ~Wire() { };
+ ~Wire();
public:
// do not simply copy wires
@@ -1216,6 +1237,10 @@ public:
RTLIL::IdString name;
int width, start_offset, port_id;
bool port_input, port_output, upto;
+
+#ifdef WITH_PYTHON
+ static std::map<unsigned int, RTLIL::Wire*> *get_all_wires(void);
+#endif
};
struct RTLIL::Memory : public RTLIL::AttrObject
@@ -1227,6 +1252,10 @@ struct RTLIL::Memory : public RTLIL::AttrObject
RTLIL::IdString name;
int width, start_offset, size;
+#ifdef WITH_PYTHON
+ ~Memory();
+ static std::map<unsigned int, RTLIL::Memory*> *get_all_memorys(void);
+#endif
};
struct RTLIL::Cell : public RTLIL::AttrObject
@@ -1238,6 +1267,7 @@ protected:
// use module->addCell() and module->remove() to create or destroy cells
friend struct RTLIL::Module;
Cell();
+ ~Cell();
public:
// do not simply copy cells
@@ -1278,9 +1308,14 @@ public:
}
template<typename T> void rewrite_sigspecs(T &functor);
+ template<typename T> void rewrite_sigspecs2(T &functor);
+
+#ifdef WITH_PYTHON
+ static std::map<unsigned int, RTLIL::Cell*> *get_all_cells(void);
+#endif
};
-struct RTLIL::CaseRule
+struct RTLIL::CaseRule : public RTLIL::AttrObject
{
std::vector<RTLIL::SigSpec> compare;
std::vector<RTLIL::SigSig> actions;
@@ -1292,6 +1327,7 @@ struct RTLIL::CaseRule
bool empty() const;
template<typename T> void rewrite_sigspecs(T &functor);
+ template<typename T> void rewrite_sigspecs2(T &functor);
RTLIL::CaseRule *clone() const;
};
@@ -1305,6 +1341,7 @@ struct RTLIL::SwitchRule : public RTLIL::AttrObject
bool empty() const;
template<typename T> void rewrite_sigspecs(T &functor);
+ template<typename T> void rewrite_sigspecs2(T &functor);
RTLIL::SwitchRule *clone() const;
};
@@ -1315,6 +1352,7 @@ struct RTLIL::SyncRule
std::vector<RTLIL::SigSig> actions;
template<typename T> void rewrite_sigspecs(T &functor);
+ template<typename T> void rewrite_sigspecs2(T &functor);
RTLIL::SyncRule *clone() const;
};
@@ -1327,6 +1365,7 @@ struct RTLIL::Process : public RTLIL::AttrObject
~Process();
template<typename T> void rewrite_sigspecs(T &functor);
+ template<typename T> void rewrite_sigspecs2(T &functor);
RTLIL::Process *clone() const;
};
@@ -1338,6 +1377,7 @@ inline RTLIL::SigBit::SigBit(RTLIL::Wire *wire) : wire(wire), offset(0) { log_as
inline RTLIL::SigBit::SigBit(RTLIL::Wire *wire, int offset) : wire(wire), offset(offset) { log_assert(wire != nullptr); }
inline RTLIL::SigBit::SigBit(const RTLIL::SigChunk &chunk) : wire(chunk.wire) { log_assert(chunk.width == 1); if (wire) offset = chunk.offset; else data = chunk.data[0]; }
inline RTLIL::SigBit::SigBit(const RTLIL::SigChunk &chunk, int index) : wire(chunk.wire) { if (wire) offset = chunk.offset + index; else data = chunk.data[index]; }
+inline RTLIL::SigBit::SigBit(const RTLIL::SigBit &sigbit) : wire(sigbit.wire), data(sigbit.data){if(wire) offset = sigbit.offset;}
inline bool RTLIL::SigBit::operator<(const RTLIL::SigBit &other) const {
if (wire == other.wire)
@@ -1388,12 +1428,30 @@ void RTLIL::Module::rewrite_sigspecs(T &functor)
}
template<typename T>
+void RTLIL::Module::rewrite_sigspecs2(T &functor)
+{
+ for (auto &it : cells_)
+ it.second->rewrite_sigspecs2(functor);
+ for (auto &it : processes)
+ it.second->rewrite_sigspecs2(functor);
+ for (auto &it : connections_) {
+ functor(it.first, it.second);
+ }
+}
+
+template<typename T>
void RTLIL::Cell::rewrite_sigspecs(T &functor) {
for (auto &it : connections_)
functor(it.second);
}
template<typename T>
+void RTLIL::Cell::rewrite_sigspecs2(T &functor) {
+ for (auto &it : connections_)
+ functor(it.second);
+}
+
+template<typename T>
void RTLIL::CaseRule::rewrite_sigspecs(T &functor) {
for (auto &it : compare)
functor(it);
@@ -1406,6 +1464,17 @@ void RTLIL::CaseRule::rewrite_sigspecs(T &functor) {
}
template<typename T>
+void RTLIL::CaseRule::rewrite_sigspecs2(T &functor) {
+ for (auto &it : compare)
+ functor(it);
+ for (auto &it : actions) {
+ functor(it.first, it.second);
+ }
+ for (auto it : switches)
+ it->rewrite_sigspecs2(functor);
+}
+
+template<typename T>
void RTLIL::SwitchRule::rewrite_sigspecs(T &functor)
{
functor(signal);
@@ -1414,6 +1483,14 @@ void RTLIL::SwitchRule::rewrite_sigspecs(T &functor)
}
template<typename T>
+void RTLIL::SwitchRule::rewrite_sigspecs2(T &functor)
+{
+ functor(signal);
+ for (auto it : cases)
+ it->rewrite_sigspecs2(functor);
+}
+
+template<typename T>
void RTLIL::SyncRule::rewrite_sigspecs(T &functor)
{
functor(signal);
@@ -1424,6 +1501,15 @@ void RTLIL::SyncRule::rewrite_sigspecs(T &functor)
}
template<typename T>
+void RTLIL::SyncRule::rewrite_sigspecs2(T &functor)
+{
+ functor(signal);
+ for (auto &it : actions) {
+ functor(it.first, it.second);
+ }
+}
+
+template<typename T>
void RTLIL::Process::rewrite_sigspecs(T &functor)
{
root_case.rewrite_sigspecs(functor);
@@ -1431,6 +1517,14 @@ void RTLIL::Process::rewrite_sigspecs(T &functor)
it->rewrite_sigspecs(functor);
}
+template<typename T>
+void RTLIL::Process::rewrite_sigspecs2(T &functor)
+{
+ root_case.rewrite_sigspecs2(functor);
+ for (auto it : syncs)
+ it->rewrite_sigspecs2(functor);
+}
+
YOSYS_NAMESPACE_END
#endif
diff --git a/kernel/yosys.cc b/kernel/yosys.cc
index 450e4e4c..69a14176 100644
--- a/kernel/yosys.cc
+++ b/kernel/yosys.cc
@@ -57,6 +57,16 @@
# include <sys/sysctl.h>
#endif
+#ifdef WITH_PYTHON
+#if PY_MAJOR_VERSION >= 3
+# define INIT_MODULE PyInit_libyosys
+ extern "C" PyObject* INIT_MODULE();
+#else
+# define INIT_MODULE initlibyosys
+ extern "C" void INIT_MODULE();
+#endif
+#endif
+
#include <limits.h>
#include <errno.h>
@@ -119,7 +129,7 @@ void yosys_banner()
log(" | |\n");
log(" | yosys -- Yosys Open SYnthesis Suite |\n");
log(" | |\n");
- log(" | Copyright (C) 2012 - 2018 Clifford Wolf <clifford@clifford.at> |\n");
+ log(" | Copyright (C) 2012 - 2019 Clifford Wolf <clifford@clifford.at> |\n");
log(" | |\n");
log(" | Permission to use, copy, modify, and/or distribute this software for any |\n");
log(" | purpose with or without fee is hereby granted, provided that the above |\n");
@@ -141,14 +151,16 @@ void yosys_banner()
int ceil_log2(int x)
{
+#if defined(__GNUC__)
+ return x > 1 ? (8*sizeof(int)) - __builtin_clz(x-1) : 0;
+#else
if (x <= 0)
return 0;
-
for (int i = 0; i < 32; i++)
if (((x-1) >> i) == 0)
return i;
-
log_abort();
+#endif
}
std::string stringf(const char *fmt, ...)
@@ -472,26 +484,61 @@ void remove_directory(std::string dirname)
#endif
}
+std::string escape_filename_spaces(const std::string& filename)
+{
+ std::string out;
+ out.reserve(filename.size());
+ for (auto c : filename)
+ {
+ if (c == ' ')
+ out += "\\ ";
+ else
+ out.push_back(c);
+ }
+ return out;
+}
+
int GetSize(RTLIL::Wire *wire)
{
return wire->width;
}
+bool already_setup = false;
+
void yosys_setup()
{
+ if(already_setup)
+ return;
+ already_setup = true;
// if there are already IdString objects then we have a global initialization order bug
IdString empty_id;
log_assert(empty_id.index_ == 0);
IdString::get_reference(empty_id.index_);
+ #ifdef WITH_PYTHON
+ PyImport_AppendInittab((char*)"libyosys", INIT_MODULE);
+ Py_Initialize();
+ PyRun_SimpleString("import sys");
+ #endif
+
Pass::init_register();
yosys_design = new RTLIL::Design;
yosys_celltypes.setup();
log_push();
}
+bool yosys_already_setup()
+{
+ return already_setup;
+}
+
+bool already_shutdown = false;
+
void yosys_shutdown()
{
+ if(already_shutdown)
+ return;
+ already_shutdown = true;
log_pop();
delete yosys_design;
@@ -519,9 +566,16 @@ void yosys_shutdown()
dlclose(it.second);
loaded_plugins.clear();
+#ifdef WITH_PYTHON
+ loaded_python_plugins.clear();
+#endif
loaded_plugin_aliases.clear();
#endif
+#ifdef WITH_PYTHON
+ Py_Finalize();
+#endif
+
IdString empty_id;
IdString::put_reference(empty_id.index_);
}
@@ -597,6 +651,10 @@ void rewrite_filename(std::string &filename)
filename = filename.substr(1, GetSize(filename)-2);
if (filename.substr(0, 2) == "+/")
filename = proc_share_dirname() + filename.substr(2);
+#ifndef _WIN32
+ if (filename.substr(0, 2) == "~/")
+ filename = filename.replace(0, 1, getenv("HOME"));
+#endif
}
#ifdef YOSYS_ENABLE_TCL
diff --git a/kernel/yosys.h b/kernel/yosys.h
index c9f97331..730efe82 100644
--- a/kernel/yosys.h
+++ b/kernel/yosys.h
@@ -66,6 +66,10 @@
#include <stdio.h>
#include <limits.h>
+#ifdef WITH_PYTHON
+#include <Python.h>
+#endif
+
#ifndef _YOSYS_
# error It looks like you are trying to build Yosys without the config defines set. \
When building Yosys with a custom make system, make sure you set all the \
@@ -83,6 +87,10 @@ extern int Tcl_EvalFile(Tcl_Interp *interp, const char *fileName);
extern void Tcl_Finalize(void);
extern int Tcl_GetCommandInfo(Tcl_Interp *interp, const char *cmdName, Tcl_CmdInfo *infoPtr);
extern const char *Tcl_GetStringResult(Tcl_Interp *interp);
+extern Tcl_Obj *Tcl_NewStringObj(const char *bytes, int length);
+extern Tcl_Obj *Tcl_NewIntObj(int intValue);
+extern Tcl_Obj *Tcl_NewListObj(int objc, Tcl_Obj *const objv[]);
+extern Tcl_Obj *Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags);
# endif
#endif
@@ -115,6 +123,7 @@ extern const char *Tcl_GetStringResult(Tcl_Interp *interp);
# define PATH_MAX 4096
#endif
+#define YOSYS_NAMESPACE Yosys
#define PRIVATE_NAMESPACE_BEGIN namespace {
#define PRIVATE_NAMESPACE_END }
#define YOSYS_NAMESPACE_BEGIN namespace Yosys {
@@ -239,7 +248,7 @@ extern bool memhasher_active;
inline void memhasher() { if (memhasher_active) memhasher_do(); }
void yosys_banner();
-int ceil_log2(int x);
+int ceil_log2(int x) YS_ATTRIBUTE(const);
std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2));
std::string vstringf(const char *fmt, va_list ap);
int readsome(std::istream &f, char *s, int n);
@@ -252,6 +261,7 @@ std::string make_temp_dir(std::string template_str = "/tmp/yosys_XXXXXX");
bool check_file_exists(std::string filename, bool is_exec = false);
bool is_absolute_path(std::string filename);
void remove_directory(std::string dirname);
+std::string escape_filename_spaces(const std::string& filename);
template<typename T> int GetSize(const T &obj) { return obj.size(); }
int GetSize(RTLIL::Wire *wire);
@@ -276,6 +286,11 @@ namespace hashlib {
}
void yosys_setup();
+
+#ifdef WITH_PYTHON
+bool yosys_already_setup();
+#endif
+
void yosys_shutdown();
#ifdef YOSYS_ENABLE_TCL
@@ -317,6 +332,9 @@ extern std::vector<RTLIL::Design*> pushed_designs;
// from passes/cmds/pluginc.cc
extern std::map<std::string, void*> loaded_plugins;
+#ifdef WITH_PYTHON
+extern std::map<std::string, void*> loaded_python_plugins;
+#endif
extern std::map<std::string, std::string> loaded_plugin_aliases;
void load_plugin(std::string filename, std::vector<std::string> aliases);
diff --git a/libs/subcircuit/subcircuit.cc b/libs/subcircuit/subcircuit.cc
index 7c723683..e8361a67 100644
--- a/libs/subcircuit/subcircuit.cc
+++ b/libs/subcircuit/subcircuit.cc
@@ -320,12 +320,10 @@ class SubCircuit::SolverWorker
static int numberOfPermutations(const std::vector<std::string> &list)
{
- int numPermutations = 1;
- for (int i = 0; i < int(list.size()); i++) {
- assert(numPermutations < maxPermutationsLimit);
- numPermutations *= i+1;
- }
- return numPermutations;
+ constexpr size_t mappedPermutationsSize = 10;
+ constexpr int mappedPermutations[mappedPermutationsSize] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
+ assert(list.size() < mappedPermutationsSize);
+ return mappedPermutations[list.size()];
}
static void permutateVectorToMap(std::map<std::string, std::string> &map, const std::vector<std::string> &list, int idx)
diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex
index e6491918..cb1bcf1b 100644
--- a/manual/CHAPTER_CellLib.tex
+++ b/manual/CHAPTER_CellLib.tex
@@ -466,6 +466,10 @@ Add information about {\tt \$assert}, {\tt \$assume}, {\tt \$live}, {\tt \$fair}
\end{fixme}
\begin{fixme}
+Add information about {\tt \$specify2}, {\tt \$specify3}, and {\tt \$specrule} cells.
+\end{fixme}
+
+\begin{fixme}
Add information about {\tt \$slice} and {\tt \$concat} cells.
\end{fixme}
diff --git a/manual/CHAPTER_Overview.tex b/manual/CHAPTER_Overview.tex
index 2feb0f1c..3009bf2c 100644
--- a/manual/CHAPTER_Overview.tex
+++ b/manual/CHAPTER_Overview.tex
@@ -331,8 +331,9 @@ to update {\tt \textbackslash{}q}.
An RTLIL::Process is a container for zero or more RTLIL::SyncRule objects and
exactly one RTLIL::CaseRule object, which is called the {\it root case}.
-An RTLIL::SyncRule object contains an (optional) synchronization condition
-(signal and edge-type) and zero or more assignments (RTLIL::SigSig).
+An RTLIL::SyncRule object contains an (optional) synchronization condition (signal and edge-type) and zero or
+more assignments (RTLIL::SigSig). The {\tt always} synchronization condition is used to break combinatorial
+loops when a latch should be inferred instead.
An RTLIL::CaseRule is a container for zero or more assignments (RTLIL::SigSig)
and zero or more RTLIL::SwitchRule objects. An RTLIL::SwitchRule objects is a
@@ -350,6 +351,18 @@ and this bit is a one (the second ``1'').} for {\tt \textbackslash{}reset == 1}
sets {\tt \$0\textbackslash{}q[0:0]} to the value of {\tt \textbackslash{}d} if {\tt
\textbackslash{}enable} is active (lines $6 \dots 11$).
+A case can specify zero or more compare values that will determine whether it matches. Each of the compare values
+must be the exact same width as the control signal. When more than one compare value is specified, the case matches
+if any of them matches the control signal; when zero compare values are specified, the case always matches (i.e.
+it is the default case).
+
+A switch prioritizes cases from first to last: multiple cases can match, but only the first matched case becomes
+active. This normally synthesizes to a priority encoder. The {\tt parallel\_case} attribute allows passes to assume
+that no more than one case will match, and {\tt full\_case} attribute allows passes to assume that exactly one
+case will match; if these invariants are ever dynamically violated, the behavior is undefined. These attributes
+are useful when an invariant invisible to the synthesizer causes the control signal to never take certain
+bit patterns.
+
The lines $13 \dots 16$ then cause {\tt \textbackslash{}q} to be updated whenever there is
a positive clock edge on {\tt \textbackslash{}clock} or {\tt \textbackslash{}reset}.
diff --git a/misc/__init__.py b/misc/__init__.py
new file mode 100644
index 00000000..330fd6d8
--- /dev/null
+++ b/misc/__init__.py
@@ -0,0 +1,5 @@
+import os
+import sys
+sys.setdlopenflags(os.RTLD_NOW | os.RTLD_GLOBAL)
+
+__all__ = ["libyosys"]
diff --git a/misc/launcher.c b/misc/launcher.c
index 157d68cf..e0d8208f 100644
--- a/misc/launcher.c
+++ b/misc/launcher.c
@@ -61,6 +61,7 @@ SOFTWARE. */
#include <windows.h>
#include <tchar.h>
#include <fcntl.h>
+#include <unistd.h>
int child_pid=0;
@@ -338,7 +339,7 @@ int run(int argc, char **argv, int is_gui) {
if (is_gui) {
/* Use exec, we don't need to wait for the GUI to finish */
- execv(ptr, (const char * const *)(newargs));
+ execv(ptr, (char * const *)(newargs));
return fail("Could not exec %s", ptr); /* shouldn't get here! */
}
diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py
new file mode 100644
index 00000000..66d661fa
--- /dev/null
+++ b/misc/py_wrap_generator.py
@@ -0,0 +1,2097 @@
+#
+# 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.
+#
+# Author Benedikt Tutzer
+#
+
+import copy
+
+#Map c++ operator Syntax to Python functions
+wrappable_operators = {
+ "<" : "__lt__",
+ "==": "__eq__",
+ "!=": "__ne__",
+ "+" : "__add__",
+ "-" : "__sub__",
+ "*" : "__mul__",
+ "/" : "__div__",
+ "()": "__call__"
+ }
+
+#Restrict certain strings from being function names in Python
+keyword_aliases = {
+ "in" : "in_",
+ "False" : "False_",
+ "None" : "None_",
+ "True" : "True_",
+ "and" : "and_",
+ "as" : "as_",
+ "assert" : "assert_",
+ "break" : "break_",
+ "class" : "class_",
+ "continue" : "continue_",
+ "def" : "def_",
+ "del" : "del_",
+ "elif" : "elif_",
+ "else" : "else_",
+ "except" : "except_",
+ "for" : "for_",
+ "from" : "from_",
+ "global" : "global_",
+ "if" : "if_",
+ "import" : "import_",
+ "in" : "in_",
+ "is" : "is_",
+ "lambda" : "lambda_",
+ "nonlocal" : "nonlocal_",
+ "not" : "not_",
+ "or" : "or_",
+ "pass" : "pass_",
+ "raise" : "raise_",
+ "return" : "return_",
+ "try" : "try_",
+ "while" : "while_",
+ "with" : "with_",
+ "yield" : "yield_"
+ }
+
+#These can be used without any explicit conversion
+primitive_types = ["void", "bool", "int", "double", "size_t", "std::string",
+ "string", "State", "char_p"]
+
+from enum import Enum
+
+#Ways to link between Python- and C++ Objects
+class link_types(Enum):
+ global_list = 1 #Manage a global list of objects in C++, the Python
+ #object contains a key to find the corresponding C++
+ #object and a Pointer to the object to verify it is
+ #still the same, making collisions unlikely to happen
+ ref_copy = 2 #The Python object contains a copy of the C++ object.
+ #The C++ object is deleted when the Python object gets
+ #deleted
+ pointer = 3 #The Python Object contains a pointer to it's C++
+ #counterpart
+ derive = 4 #The Python-Wrapper is derived from the C++ object.
+
+class attr_types(Enum):
+ star = "*"
+ amp = "&"
+ ampamp = "&&"
+ default = ""
+
+#For source-files
+class Source:
+ name = ""
+ classes = []
+
+ def __init__(self, name, classes):
+ self.name = name
+ self.classes = classes
+
+#Splits a list by the given delimiter, without splitting strings inside
+#pointy-brackets (< and >)
+def split_list(str_def, delim):
+ str_def = str_def.strip()
+ if len(str_def) == 0:
+ return []
+ if str_def.count(delim) == 0:
+ return [str_def]
+ if str_def.count("<") == 0:
+ return str_def.split(delim)
+ if str_def.find("<") < str_def.find(" "):
+ closing = find_closing(str_def[str_def.find("<")+1:], "<", ">") + str_def.find("<")
+ comma = str_def[closing:].find(delim)
+ if comma == -1:
+ return [str_def]
+ comma = closing + comma
+ else:
+ comma = str_def.find(delim)
+ rest = split_list(str_def[comma+1:], delim)
+ ret = [str_def[:comma]]
+ if rest != None and len(rest) != 0:
+ ret.extend(rest)
+ return ret
+
+#Represents a Type
+class WType:
+ name = ""
+ cont = None
+ attr_type = attr_types.default
+
+ def __init__(self, name = "", cont = None, attr_type = attr_types.default):
+ self.name = name
+ self.cont = cont
+ self.attr_type = attr_type
+
+ #Python type-string
+ def gen_text(self):
+ text = self.name
+ if self.name in enum_names:
+ text = enum_by_name(self.name).namespace + "::" + self.name
+ if self.cont != None:
+ return known_containers[self.name].typename
+ return text
+
+ #C++ type-string
+ def gen_text_cpp(self):
+ postfix = ""
+ if self.attr_type == attr_types.star:
+ postfix = "*"
+ if self.name in primitive_types:
+ return self.name + postfix
+ if self.name in enum_names:
+ return enum_by_name(self.name).namespace + "::" + self.name + postfix
+ if self.name in classnames:
+ return class_by_name(self.name).namespace + "::" + self.name + postfix
+ text = self.name
+ if self.cont != None:
+ text += "<"
+ for a in self.cont.args:
+ text += a.gen_text_cpp() + ", "
+ text = text[:-2]
+ text += ">"
+ return text
+
+ @staticmethod
+ def from_string(str_def, containing_file, line_number):
+ str_def = str_def.strip()
+ if len(str_def) == 0:
+ return None
+ str_def = str_def.replace("RTLIL::SigSig", "std::pair<SigSpec, SigSpec>").replace("SigSig", "std::pair<SigSpec, SigSpec>")
+ t = WType()
+ t.name = ""
+ t.cont = None
+ t.attr_type = attr_types.default
+ if str_def.find("<") != -1:# and str_def.find("<") < str_def.find(" "):
+ candidate = WContainer.from_string(str_def, containing_file, line_number)
+ if candidate == None:
+ return None
+ t.name = str_def[:str_def.find("<")]
+
+ if t.name.count("*") + t.name.count("&") > 1:
+ return None
+
+ if t.name.count("*") == 1 or str_def[0] == '*' or str_def[-1] == '*':
+ t.attr_type = attr_types.star
+ t.name = t.name.replace("*","")
+ elif t.name.count("&&") == 1:
+ t.attr_type = attr_types.ampamp
+ t.name = t.name.replace("&&","")
+ elif t.name.count("&") == 1 or str_def[0] == '&' or str_def[-1] == '&':
+ t.attr_type = attr_types.amp
+ t.name = t.name.replace("&","")
+
+ t.cont = candidate
+ if(t.name not in known_containers):
+ return None
+ return t
+
+ prefix = ""
+
+ if str.startswith(str_def, "unsigned "):
+ prefix = "unsigned "
+ str_def = str_def[9:]
+ while str.startswith(str_def, "long "):
+ prefix= "long " + prefix
+ str_def = str_def[5:]
+ while str.startswith(str_def, "short "):
+ prefix = "short " + prefix
+ str_def = str_def[6:]
+
+ str_def = str_def.split("::")[-1]
+
+ if str_def.count("*") + str_def.count("&") >= 2:
+ return None
+
+ if str_def.count("*") == 1:
+ t.attr_type = attr_types.star
+ str_def = str_def.replace("*","")
+ elif str_def.count("&&") == 1:
+ t.attr_type = attr_types.ampamp
+ str_def = str_def.replace("&&","")
+ elif str_def.count("&") == 1:
+ t.attr_type = attr_types.amp
+ str_def = str_def.replace("&","")
+
+ if len(str_def) > 0 and str_def.split("::")[-1] not in primitive_types and str_def.split("::")[-1] not in classnames and str_def.split("::")[-1] not in enum_names:
+ return None
+
+ if str_def.count(" ") == 0:
+ t.name = (prefix + str_def).replace("char_p", "char *")
+ t.cont = None
+ return t
+ return None
+
+#Represents a container-type
+class WContainer:
+ name = ""
+ args = []
+
+ def from_string(str_def, containing_file, line_number):
+ if str_def == None or len(str_def) < 4:
+ return None
+ cont = WContainer()
+ cont.name = str_def[:str_def.find("<")]
+ str_def = str_def[str_def.find("<")+1:find_closing(str_def, "<", ">")]
+ cont.args = []
+ for arg in split_list(str_def, ","):
+ candidate = WType.from_string(arg.strip(), containing_file, line_number)
+ if candidate == None:
+ return None
+ cont.args.append(candidate)
+ return cont
+
+#Translators between Python and C++ containers
+#Base Type
+class Translator:
+ tmp_cntr = 0
+ typename = "DefaultType"
+ orig_name = "DefaultCpp"
+
+ @classmethod
+ def gen_type(c, types):
+ return "\nImplement a function that outputs the c++ type of this container here\n"
+
+ @classmethod
+ def translate(c, varname, types, prefix):
+ return "\nImplement a function translating a python container to a c++ container here\n"
+
+ @classmethod
+ def translate_cpp(c, varname, types, prefix, ref):
+ return "\nImplement a function translating a c++ container to a python container here\n"
+
+#Translates list-types (vector, pool, set), that only differ in their name and
+#the name of the insertion function
+class PythonListTranslator(Translator):
+ typename = "boost::python::list"
+ insert_name = "Default"
+
+ #generate the c++ type string
+ @classmethod
+ def gen_type(c, types):
+ text = c.orig_name + "<"
+ if types[0].name in primitive_types:
+ text += types[0].name
+ elif types[0].name in known_containers:
+ text += known_containers[types[0].name].gen_type(types[0].cont.args)
+ else:
+ text += class_by_name(types[0].name).namespace + "::" + types[0].name
+ if types[0].attr_type == attr_types.star:
+ text += "*"
+ text += ">"
+ return text
+
+ #Generate C++ code to translate from a boost::python::list
+ @classmethod
+ def translate(c, varname, types, prefix):
+ text = prefix + c.gen_type(types) + " " + varname + "___tmp;"
+ cntr_name = "cntr_" + str(Translator.tmp_cntr)
+ Translator.tmp_cntr = Translator.tmp_cntr + 1
+ text += prefix + "for(int " + cntr_name + " = 0; " + cntr_name + " < len(" + varname + "); " + cntr_name + "++)"
+ text += prefix + "{"
+ tmp_name = "tmp_" + str(Translator.tmp_cntr)
+ Translator.tmp_cntr = Translator.tmp_cntr + 1
+ if types[0].name in known_containers:
+ text += prefix + "\t" + known_containers[types[0].name].typename + " " + tmp_name + " = boost::python::extract<" + known_containers[types[0].name].typename + ">(" + varname + "[" + cntr_name + "]);"
+ text += known_containers[types[0].name].translate(tmp_name, types[0].cont.args, prefix+"\t")
+ tmp_name = tmp_name + "___tmp"
+ text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(" + tmp_name + ");"
+ elif types[0].name in classnames:
+ text += prefix + "\t" + types[0].name + "* " + tmp_name + " = boost::python::extract<" + types[0].name + "*>(" + varname + "[" + cntr_name + "]);"
+ if types[0].attr_type == attr_types.star:
+ text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(" + tmp_name + "->get_cpp_obj());"
+ else:
+ text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(*" + tmp_name + "->get_cpp_obj());"
+ else:
+ text += prefix + "\t" + types[0].name + " " + tmp_name + " = boost::python::extract<" + types[0].name + ">(" + varname + "[" + cntr_name + "]);"
+ text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(" + tmp_name + ");"
+ text += prefix + "}"
+ return text
+
+ #Generate C++ code to translate to a boost::python::list
+ @classmethod
+ def translate_cpp(c, varname, types, prefix, ref):
+ text = prefix + c.typename + " " + varname + "___tmp;"
+ tmp_name = "tmp_" + str(Translator.tmp_cntr)
+ Translator.tmp_cntr = Translator.tmp_cntr + 1
+ if ref:
+ text += prefix + "for(auto " + tmp_name + " : *" + varname + ")"
+ else:
+ text += prefix + "for(auto " + tmp_name + " : " + varname + ")"
+ text += prefix + "{"
+ if types[0].name in classnames:
+ if types[0].attr_type == attr_types.star:
+ text += prefix + "\t" + varname + "___tmp.append(" + types[0].name + "::get_py_obj(" + tmp_name + "));"
+ else:
+ text += prefix + "\t" + varname + "___tmp.append(*" + types[0].name + "::get_py_obj(&" + tmp_name + "));"
+ elif types[0].name in known_containers:
+ text += known_containers[types[0].name].translate_cpp(tmp_name, types[0].cont.args, prefix + "\t", types[0].attr_type == attr_types.star)
+ text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + "___tmp);"
+ else:
+ text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + ");"
+ text += prefix + "}"
+ return text
+
+#Sub-type for std::set
+class SetTranslator(PythonListTranslator):
+ insert_name = "insert"
+ orig_name = "std::set"
+
+#Sub-type for std::vector
+class VectorTranslator(PythonListTranslator):
+ insert_name = "push_back"
+ orig_name = "std::vector"
+
+#Sub-type for pool
+class PoolTranslator(PythonListTranslator):
+ insert_name = "insert"
+ orig_name = "pool"
+
+#Translates dict-types (dict, std::map), that only differ in their name and
+#the name of the insertion function
+class PythonDictTranslator(Translator):
+ typename = "boost::python::dict"
+ insert_name = "Default"
+
+ @classmethod
+ def gen_type(c, types):
+ text = c.orig_name + "<"
+ if types[0].name in primitive_types:
+ text += types[0].name
+ elif types[0].name in known_containers:
+ text += known_containers[types[0].name].gen_type(types[0].cont.args)
+ else:
+ text += class_by_name(types[0].name).namespace + "::" + types[0].name
+ if types[0].attr_type == attr_types.star:
+ text += "*"
+ text += ", "
+ if types[1].name in primitive_types:
+ text += types[1].name
+ elif types[1].name in known_containers:
+ text += known_containers[types[1].name].gen_type(types[1].cont.args)
+ else:
+ text += class_by_name(types[1].name).namespace + "::" + types[1].name
+ if types[1].attr_type == attr_types.star:
+ text += "*"
+ text += ">"
+ return text
+
+ #Generate c++ code to translate from a boost::python::dict
+ @classmethod
+ def translate(c, varname, types, prefix):
+ text = prefix + c.gen_type(types) + " " + varname + "___tmp;"
+ text += prefix + "boost::python::list " + varname + "_keylist = " + varname + ".keys();"
+ cntr_name = "cntr_" + str(Translator.tmp_cntr)
+ Translator.tmp_cntr = Translator.tmp_cntr + 1
+ text += prefix + "for(int " + cntr_name + " = 0; " + cntr_name + " < len(" + varname + "_keylist); " + cntr_name + "++)"
+ text += prefix + "{"
+ key_tmp_name = "key_tmp_" + str(Translator.tmp_cntr)
+ val_tmp_name = "val_tmp_" + str(Translator.tmp_cntr)
+ Translator.tmp_cntr = Translator.tmp_cntr + 1
+
+ if types[0].name in known_containers:
+ text += prefix + "\t" + known_containers[types[0].name].typename + " " + key_tmp_name + " = boost::python::extract<" + known_containers[types[0].name].typename + ">(" + varname + "_keylist[ " + cntr_name + " ]);"
+ text += known_containers[types[0].name].translate(key_tmp_name, types[0].cont.args, prefix+"\t")
+ key_tmp_name = key_tmp_name + "___tmp"
+ elif types[0].name in classnames:
+ text += prefix + "\t" + types[0].name + "* " + key_tmp_name + " = boost::python::extract<" + types[0].name + "*>(" + varname + "_keylist[ " + cntr_name + " ]);"
+ else:
+ text += prefix + "\t" + types[0].name + " " + key_tmp_name + " = boost::python::extract<" + types[0].name + ">(" + varname + "_keylist[ " + cntr_name + " ]);"
+
+ if types[1].name in known_containers:
+ text += prefix + "\t" + known_containers[types[1].name].typename + " " + val_tmp_name + " = boost::python::extract<" + known_containers[types[1].name].typename + ">(" + varname + "[" + varname + "_keylist[ " + cntr_name + " ]]);"
+ text += known_containers[types[1].name].translate(val_tmp_name, types[1].cont.args, prefix+"\t")
+ val_tmp_name = val_tmp_name + "___tmp"
+ elif types[1].name in classnames:
+ text += prefix + "\t" + types[1].name + "* " + val_tmp_name + " = boost::python::extract<" + types[1].name + "*>(" + varname + "[" + varname + "_keylist[ " + cntr_name + " ]]);"
+ else:
+ text += prefix + "\t" + types[1].name + " " + val_tmp_name + " = boost::python::extract<" + types[1].name + ">(" + varname + "[" + varname + "_keylist[ " + cntr_name + " ]]);"
+
+ text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(std::pair<" + types[0].gen_text_cpp() + ", " + types[1].gen_text_cpp() + ">("
+
+ if types[0].name not in classnames:
+ text += key_tmp_name
+ else:
+ if types[0].attr_type != attr_types.star:
+ text += "*"
+ text += key_tmp_name + "->get_cpp_obj()"
+
+ text += ", "
+ if types[1].name not in classnames:
+ text += val_tmp_name
+ else:
+ if types[1].attr_type != attr_types.star:
+ text += "*"
+ text += val_tmp_name + "->get_cpp_obj()"
+ text += "));\n" + prefix + "}"
+ return text
+
+ #Generate c++ code to translate to a boost::python::dict
+ @classmethod
+ def translate_cpp(c, varname, types, prefix, ref):
+ text = prefix + c.typename + " " + varname + "___tmp;"
+ tmp_name = "tmp_" + str(Translator.tmp_cntr)
+ Translator.tmp_cntr = Translator.tmp_cntr + 1
+ if ref:
+ text += prefix + "for(auto " + tmp_name + " : *" + varname + ")"
+ else:
+ text += prefix + "for(auto " + tmp_name + " : " + varname + ")"
+ text += prefix + "{"
+ if types[1].name in known_containers:
+ text += prefix + "\tauto " + tmp_name + "_second = " + tmp_name + ".second;"
+ text += known_containers[types[1].name].translate_cpp(tmp_name + "_second", types[1].cont.args, prefix + "\t", types[1].attr_type == attr_types.star)
+
+ if types[0].name in classnames:
+ text += prefix + "\t" + varname + "___tmp[" + types[0].name + "::get_py_obj(" + tmp_name + ".first)] = "
+ elif types[0].name not in known_containers:
+ text += prefix + "\t" + varname + "___tmp[" + tmp_name + ".first] = "
+
+ if types[1].name in classnames:
+ if types[1].attr_type == attr_types.star:
+ text += types[1].name + "::get_py_obj(" + tmp_name + ".second);"
+ else:
+ text += "*" + types[1].name + "::get_py_obj(&" + tmp_name + ".second);"
+ elif types[1].name in known_containers:
+ text += tmp_name + "_second___tmp;"
+ else:
+ text += tmp_name + ".second;"
+ text += prefix + "}"
+ return text
+
+#Sub-type for dict
+class DictTranslator(PythonDictTranslator):
+ insert_name = "insert"
+ orig_name = "dict"
+
+#Sub_type for std::map
+class MapTranslator(PythonDictTranslator):
+ insert_name = "insert"
+ orig_name = "std::map"
+
+#Translator for std::pair. Derived from PythonDictTranslator because the
+#gen_type function is the same (because both have two template parameters)
+class TupleTranslator(PythonDictTranslator):
+ typename = "boost::python::tuple"
+ orig_name = "std::pair"
+
+ #Generate c++ code to translate from a boost::python::tuple
+ @classmethod
+ def translate(c, varname, types, prefix):
+ text = prefix + types[0].name + " " + varname + "___tmp_0 = boost::python::extract<" + types[0].name + ">(" + varname + "[0]);"
+ text += prefix + types[1].name + " " + varname + "___tmp_1 = boost::python::extract<" + types[1].name + ">(" + varname + "[1]);"
+ text += prefix + TupleTranslator.gen_type(types) + " " + varname + "___tmp("
+ if types[0].name.split(" ")[-1] in primitive_types:
+ text += varname + "___tmp_0, "
+ else:
+ text += varname + "___tmp_0.get_cpp_obj(), "
+ if types[1].name.split(" ")[-1] in primitive_types:
+ text += varname + "___tmp_1);"
+ else:
+ text += varname + "___tmp_1.get_cpp_obj());"
+ return text
+
+ #Generate c++ code to translate to a boost::python::tuple
+ @classmethod
+ def translate_cpp(c, varname, types, prefix, ref):
+ text = prefix + TupleTranslator.typename + " " + varname + "___tmp = boost::python::make_tuple(" + varname + ".first, " + varname + ".second);"
+ return text
+ tmp_name = "tmp_" + str(Translator.tmp_cntr)
+ Translator.tmp_cntr = Translator.tmp_cntr + 1
+ if ref:
+ text += prefix + "for(auto " + tmp_name + " : *" + varname + ")"
+ else:
+ text += prefix + "for(auto " + tmp_name + " : " + varname + ")"
+ text += prefix + "{"
+ if types[0].name.split(" ")[-1] in primitive_types or types[0].name in enum_names:
+ text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + ");"
+ elif types[0].name in known_containers:
+ text += known_containers[types[0].name].translate_cpp(tmp_name, types[0].cont.args, prefix + "\t", types[1].attr_type == attr_types.star)
+ text += prefix + "\t" + varname + "___tmp.append(" + types[0].name + "::get_py_obj(" + tmp_name + "___tmp);"
+ elif types[0].name in classnames:
+ text += prefix + "\t" + varname + "___tmp.append(" + types[0].name + "::get_py_obj(" + tmp_name + "));"
+ text += prefix + "}"
+ return text
+
+#Associate the Translators with their c++ type
+known_containers = {
+ "std::set" : SetTranslator,
+ "std::vector" : VectorTranslator,
+ "pool" : PoolTranslator,
+ "dict" : DictTranslator,
+ "std::pair" : TupleTranslator,
+ "std::map" : MapTranslator
+}
+
+class Attribute:
+ wtype = None
+ varname = None
+ is_const = False
+ default_value = None
+ pos = None
+ pos_counter = 0
+
+ def __init__(self, wtype, varname, is_const = False, default_value = None):
+ self.wtype = wtype
+ self.varname = varname
+ self.is_const = is_const
+ self.default_value = None
+ self.container = None
+
+ @staticmethod
+ def from_string(str_def, containing_file, line_number):
+ if len(str_def) < 3:
+ return None
+ orig = str_def
+ arg = Attribute(None, None)
+ prefix = ""
+ arg.wtype = None
+ arg.varname = None
+ arg.is_const = False
+ arg.default_value = None
+ arg.container = None
+ if str.startswith(str_def, "const "):
+ arg.is_const = True
+ str_def = str_def[6:]
+ if str.startswith(str_def, "unsigned "):
+ prefix = "unsigned "
+ str_def = str_def[9:]
+ while str.startswith(str_def, "long "):
+ prefix= "long " + prefix
+ str_def = str_def[5:]
+ while str.startswith(str_def, "short "):
+ prefix = "short " + prefix
+ str_def = str_def[6:]
+
+ if str_def.find("<") != -1 and str_def.find("<") < str_def.find(" "):
+ closing = find_closing(str_def[str_def.find("<"):], "<", ">") + str_def.find("<") + 1
+ arg.wtype = WType.from_string(str_def[:closing].strip(), containing_file, line_number)
+ str_def = str_def[closing+1:]
+ else:
+ if str_def.count(" ") > 0:
+ arg.wtype = WType.from_string(prefix + str_def[:str_def.find(" ")].strip(), containing_file, line_number)
+ str_def = str_def[str_def.find(" ")+1:]
+ else:
+ arg.wtype = WType.from_string(prefix + str_def.strip(), containing_file, line_number)
+ str_def = ""
+ arg.varname = ""
+
+ if arg.wtype == None:
+ return None
+ if str_def.count("=") == 0:
+ arg.varname = str_def.strip()
+ if arg.varname.find(" ") > 0:
+ return None
+ else:
+ arg.varname = str_def[:str_def.find("=")].strip()
+ if arg.varname.find(" ") > 0:
+ return None
+ str_def = str_def[str_def.find("=")+1:].strip()
+ arg.default_value = str_def[arg.varname.find("=")+1:].strip()
+ if len(arg.varname) == 0:
+ arg.varname = None
+ return arg
+ if arg.varname[0] == '*':
+ arg.wtype.attr_type = attr_types.star
+ arg.varname = arg.varname[1:]
+ elif arg.varname[0] == '&':
+ if arg.wtype.attr_type != attr_types.default:
+ return None
+ if arg.varname[1] == '&':
+ arg.wtype.attr_type = attr_types.ampamp
+ arg.varname = arg.varname[2:]
+ else:
+ arg.wtype.attr_type = attr_types.amp
+ arg.varname = arg.varname[1:]
+ return arg
+
+ #Generates the varname. If the attribute has no name in the header file,
+ #a name is generated
+ def gen_varname(self):
+ if self.varname != None:
+ return self.varname
+ if self.wtype.name == "void":
+ return ""
+ if self.pos == None:
+ self.pos = Attribute.pos_counter
+ Attribute.pos_counter = Attribute.pos_counter + 1
+ return "gen_varname_" + str(self.pos)
+
+ #Generates the text for the function headers with wrapper types
+ def gen_listitem(self):
+ prefix = ""
+ if self.is_const:
+ prefix = "const "
+ if self.wtype.name in classnames:
+ return prefix + self.wtype.name + "* " + self.gen_varname()
+ if self.wtype.name in known_containers:
+ return prefix + known_containers[self.wtype.name].typename + " " + self.gen_varname()
+ return prefix + self.wtype.name + " " + self.gen_varname()
+
+ #Generates the test for the function headers with c++ types
+ def gen_listitem_cpp(self):
+ prefix = ""
+ if self.is_const:
+ prefix = "const "
+ infix = ""
+ if self.wtype.attr_type == attr_types.star:
+ infix = "*"
+ elif self.wtype.attr_type == attr_types.amp:
+ infix = "&"
+ elif self.wtype.attr_type == attr_types.ampamp:
+ infix = "&&"
+ if self.wtype.name in known_containers:
+ return prefix + known_containers[self.wtype.name].gen_type(self.wtype.cont.args) + " " + infix + self.gen_varname()
+ if self.wtype.name in classnames:
+ return prefix + class_by_name(self.wtype.name).namespace + "::" + self.wtype.name + " " + infix + self.gen_varname()
+ return prefix + self.wtype.name + " " + infix + self.gen_varname()
+
+ #Generates the listitem withtout the varname, so the signature can be
+ #compared
+ def gen_listitem_hash(self):
+ prefix = ""
+ if self.is_const:
+ prefix = "const "
+ if self.wtype.name in classnames:
+ return prefix + self.wtype.name + "* "
+ if self.wtype.name in known_containers:
+ return known_containers[self.wtype.name].typename
+ return prefix + self.wtype.name
+
+ #Generate Translation code for the attribute
+ def gen_translation(self):
+ if self.wtype.name in known_containers:
+ return known_containers[self.wtype.name].translate(self.gen_varname(), self.wtype.cont.args, "\n\t\t")
+ return ""
+
+ #Generate Translation code from c++ for the attribute
+ def gen_translation_cpp(self):
+ if self.wtype.name in known_containers:
+ return known_containers[self.wtype.name].translate_cpp(self.gen_varname(), self.wtype.cont.args, "\n\t\t", self.wtype.attr_type == attr_types.star)
+ return ""
+
+ #Generate Text for the call
+ def gen_call(self):
+ ret = self.gen_varname()
+ if self.wtype.name in known_containers:
+ if self.wtype.attr_type == attr_types.star:
+ return "&" + ret + "___tmp"
+ return ret + "___tmp"
+ if self.wtype.name in classnames:
+ if self.wtype.attr_type != attr_types.star:
+ ret = "*" + ret
+ return ret + "->get_cpp_obj()"
+ if self.wtype.name == "char *" and self.gen_varname() in ["format", "fmt"]:
+ return "\"%s\", " + self.gen_varname()
+ if self.wtype.attr_type == attr_types.star:
+ return "&" + ret
+ return ret
+
+ def gen_call_cpp(self):
+ ret = self.gen_varname()
+ if self.wtype.name.split(" ")[-1] in primitive_types or self.wtype.name in enum_names:
+ if self.wtype.attr_type == attr_types.star:
+ return "&" + ret
+ return ret
+ if self.wtype.name not in classnames:
+ if self.wtype.attr_type == attr_types.star:
+ return "&" + ret + "___tmp"
+ return ret + "___tmp"
+ if self.wtype.attr_type != attr_types.star:
+ ret = "*" + ret
+ return self.wtype.name + "::get_py_obj(" + self.gen_varname() + ")"
+
+ #Generate cleanup code
+ def gen_cleanup(self):
+ if self.wtype.name in primitive_types or self.wtype.name in classnames or self.wtype.name in enum_names or not self.wtype.attr_type == attr_types.star or (self.wtype.name in known_containers and self.wtype.attr_type == attr_types.star):
+ return ""
+ return "\n\t\tdelete " + self.gen_varname() + "___tmp;"
+
+class WClass:
+ name = None
+ namespace = None
+ link_type = None
+ id_ = None
+ string_id = None
+ hash_id = None
+ needs_clone = False
+ found_funs = []
+ found_vars = []
+ found_constrs = []
+
+ def __init__(self, name, link_type, id_, string_id = None, hash_id = None, needs_clone = False):
+ self.name = name
+ self.namespace = None
+ self.link_type = link_type
+ self.id_ = id_
+ self.string_id = string_id
+ self.hash_id = hash_id
+ self.needs_clone = needs_clone
+ self.found_funs = []
+ self.found_vars = []
+ self.found_constrs = []
+
+ def printable_constrs(self):
+ ret = 0
+ for con in self.found_constrs:
+ if not con.protected:
+ ret += 1
+ return ret
+
+ def gen_decl(self, filename):
+ long_name = self.namespace + "::" + self.name
+
+ text = "\n\t// WRAPPED from " + filename
+ text += "\n\tstruct " + self.name
+ if self.link_type == link_types.derive:
+ text += " : public " + self.namespace + "::" + self.name
+ text += "\n\t{\n"
+
+ if self.link_type != link_types.derive:
+
+ text += "\t\t" + long_name + "* ref_obj;\n"
+
+ if self.link_type == link_types.ref_copy or self.link_type == link_types.pointer:
+ text += "\n\t\t" + long_name + "* get_cpp_obj() const\n\t\t{\n\t\t\treturn ref_obj;\n\t\t}\n"
+ elif self.link_type == link_types.global_list:
+ text += "\t\t" + self.id_.wtype.name + " " + self.id_.varname + ";\n"
+ text += "\n\t\t" + long_name + "* get_cpp_obj() const\n\t\t{"
+ text += "\n\t\t\t" + long_name + "* ret = " + long_name + "::get_all_" + self.name.lower() + "s()->at(this->" + self.id_.varname + ");"
+ text += "\n\t\t\tif(ret != NULL && ret == this->ref_obj)"
+ text += "\n\t\t\t\treturn ret;"
+ text += "\n\t\t\tthrow std::runtime_error(\"" + self.name + "'s c++ object does not exist anymore.\");"
+ text += "\n\t\t\treturn NULL;"
+ text += "\n\t\t}\n"
+
+ #if self.link_type != link_types.pointer:
+ text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + "* ref)\n\t\t{"
+ text += "\n\t\t\tif(ref == nullptr){"
+ text += "\n\t\t\t\tthrow std::runtime_error(\"" + self.name + " does not exist.\");"
+ text += "\n\t\t\t}"
+ text += "\n\t\t\t" + self.name + "* ret = (" + self.name + "*)malloc(sizeof(" + self.name + "));"
+ if self.link_type == link_types.pointer:
+ text += "\n\t\t\tret->ref_obj = ref;"
+ if self.link_type == link_types.ref_copy:
+ if self.needs_clone:
+ text += "\n\t\t\tret->ref_obj = ref->clone();"
+ else:
+ text += "\n\t\t\tret->ref_obj = new "+long_name+"(*ref);"
+ if self.link_type == link_types.global_list:
+ text += "\n\t\t\tret->ref_obj = ref;"
+ text += "\n\t\t\tret->" + self.id_.varname + " = ret->ref_obj->" + self.id_.varname + ";"
+ text += "\n\t\t\treturn ret;"
+ text += "\n\t\t}\n"
+
+ if self.link_type == link_types.ref_copy:
+ text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + " ref)\n\t\t{"
+ text += "\n\t\t\t" + self.name + "* ret = (" + self.name + "*)malloc(sizeof(" + self.name + "));"
+ if self.needs_clone:
+ text += "\n\t\t\tret->ref_obj = ref.clone();"
+ else:
+ text += "\n\t\t\tret->ref_obj = new "+long_name+"(ref);"
+ text += "\n\t\t\treturn ret;"
+ text += "\n\t\t}\n"
+
+ for con in self.found_constrs:
+ text += con.gen_decl()
+ for var in self.found_vars:
+ text += var.gen_decl()
+ for fun in self.found_funs:
+ text += fun.gen_decl()
+
+
+ if self.link_type == link_types.derive:
+ duplicates = {}
+ for fun in self.found_funs:
+ if fun.name in duplicates:
+ fun.gen_alias()
+ duplicates[fun.name].gen_alias()
+ else:
+ duplicates[fun.name] = fun
+
+ text += "\n\t\t" + long_name + "* get_cpp_obj() const\n\t\t{\n\t\t\treturn (" + self.namespace + "::" + self.name +"*)this;\n\t\t}\n"
+ text += "\n\t\tstatic " + self.name + "* get_py_obj(" + long_name + "* ref)\n\t\t{"
+ text += "\n\t\t\treturn (" + self.name + "*)ref;"
+ text += "\n\t\t}\n"
+
+ for con in self.found_constrs:
+ text += con.gen_decl_derive()
+ for var in self.found_vars:
+ text += var.gen_decl()
+ for fun in self.found_funs:
+ text += fun.gen_decl_virtual()
+
+ if self.hash_id != None:
+ text += "\n\t\tunsigned int get_hash_py()"
+ text += "\n\t\t{"
+ text += "\n\t\t\treturn get_cpp_obj()->" + self.hash_id + ";"
+ text += "\n\t\t}"
+
+ text += "\n\t};\n"
+
+ if self.link_type == link_types.derive:
+ text += "\n\tstruct " + self.name + "Wrap : " + self.name + ", boost::python::wrapper<" + self.name + ">"
+ text += "\n\t{"
+
+ for con in self.found_constrs:
+ text += con.gen_decl_wrapperclass()
+ for fun in self.found_funs:
+ text += fun.gen_default_impl()
+
+ text += "\n\t};"
+
+ text += "\n\tstd::ostream &operator<<(std::ostream &ostr, const " + self.name + " &ref)"
+ text += "\n\t{"
+ text += "\n\t\tostr << \"" + self.name
+ if self.string_id != None:
+ text +=" \\\"\""
+ text += " << ref.get_cpp_obj()->" + self.string_id
+ text += " << \"\\\"\""
+ else:
+ text += " at \" << ref.get_cpp_obj()"
+ text += ";"
+ text += "\n\t\treturn ostr;"
+ text += "\n\t}"
+ text += "\n"
+
+ return text
+
+ def gen_funs(self, filename):
+ text = ""
+ if self.link_type != link_types.derive:
+ for con in self.found_constrs:
+ text += con.gen_def()
+ for var in self.found_vars:
+ text += var.gen_def()
+ for fun in self.found_funs:
+ text += fun.gen_def()
+ else:
+ for var in self.found_vars:
+ text += var.gen_def()
+ for fun in self.found_funs:
+ text += fun.gen_def_virtual()
+ return text
+
+ def gen_boost_py(self):
+ text = "\n\t\tclass_<" + self.name
+ if self.link_type == link_types.derive:
+ text += "Wrap, boost::noncopyable"
+ text += ">(\"" + self.name + "\""
+ if self.printable_constrs() == 0 or not self.contains_default_constr():
+ text += ", no_init"
+ text += ")"
+ text += "\n\t\t\t.def(boost::python::self_ns::str(boost::python::self_ns::self))"
+ text += "\n\t\t\t.def(boost::python::self_ns::repr(boost::python::self_ns::self))"
+ for con in self.found_constrs:
+ text += con.gen_boost_py()
+ for var in self.found_vars:
+ text += var.gen_boost_py()
+ static_funs = []
+ for fun in self.found_funs:
+ text += fun.gen_boost_py()
+ if fun.is_static and fun.alias not in static_funs:
+ static_funs.append(fun.alias)
+ for fun in static_funs:
+ text += "\n\t\t\t.staticmethod(\"" + fun + "\")"
+
+ if self.hash_id != None:
+ text += "\n\t\t\t.def(\"__hash__\", &" + self.name + "::get_hash_py)"
+ text += "\n\t\t\t;\n"
+ return text
+
+ def contains_default_constr(self):
+ for c in self.found_constrs:
+ if len(c.args) == 0:
+ return True
+ return False
+
+#CONFIGURE HEADER-FILES TO BE PARSED AND CLASSES EXPECTED IN THEM HERE
+
+sources = [
+ Source("kernel/celltypes",[
+ WClass("CellType", link_types.pointer, None, None, "type.hash()", True),
+ WClass("CellTypes", link_types.pointer, None, None, None, True)
+ ]
+ ),
+ Source("kernel/consteval",[
+ WClass("ConstEval", link_types.pointer, None, None, None, True)
+ ]
+ ),
+ Source("kernel/log",[]),
+ Source("kernel/register",[
+ WClass("Pass", link_types.derive, None, None, None, True),
+ ]
+ ),
+ Source("kernel/rtlil",[
+ WClass("IdString", link_types.ref_copy, None, "str()", "hash()"),
+ WClass("Const", link_types.ref_copy, None, "as_string()", "hash()"),
+ WClass("AttrObject", link_types.ref_copy, None, None, None),
+ WClass("Selection", link_types.ref_copy, None, None, None),
+ WClass("Monitor", link_types.derive, None, None, None),
+ WClass("CaseRule",link_types.ref_copy, None, None, None, True),
+ WClass("SwitchRule",link_types.ref_copy, None, None, None, True),
+ WClass("SyncRule", link_types.ref_copy, None, None, None, True),
+ WClass("Process", link_types.ref_copy, None, "name.c_str()", "name.hash()"),
+ WClass("SigChunk", link_types.ref_copy, None, None, None),
+ WClass("SigBit", link_types.ref_copy, None, None, "hash()"),
+ WClass("SigSpec", link_types.ref_copy, None, None, "hash()"),
+ WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
+ WClass("Wire", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
+ WClass("Memory", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
+ WClass("Module", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
+ WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "hash()")
+ ]
+ ),
+ #Source("kernel/satgen",[
+ # ]
+ # ),
+ #Source("libs/ezsat/ezsat",[
+ # ]
+ # ),
+ #Source("libs/ezsat/ezminisat",[
+ # ]
+ # ),
+ Source("kernel/sigtools",[
+ WClass("SigMap", link_types.pointer, None, None, None, True)
+ ]
+ ),
+ Source("kernel/yosys",[
+ ]
+ ),
+ Source("kernel/cost",[])
+ ]
+
+blacklist_methods = ["YOSYS_NAMESPACE::Pass::run_register", "YOSYS_NAMESPACE::Module::Pow", "YOSYS_NAMESPACE::Module::Bu0", "YOSYS_NAMESPACE::CaseRule::optimize"]
+
+enum_names = ["State","SyncType","ConstFlags"]
+
+enums = [] #Do not edit
+
+unowned_functions = []
+
+classnames = []
+for source in sources:
+ for wclass in source.classes:
+ classnames.append(wclass.name)
+
+def class_by_name(name):
+ for source in sources:
+ for wclass in source.classes:
+ if wclass.name == name:
+ return wclass
+ return None
+
+def enum_by_name(name):
+ for e in enums:
+ if e.name == name:
+ return e
+ return None
+
+def find_closing(text, open_tok, close_tok):
+ if text.find(open_tok) == -1 or text.find(close_tok) <= text.find(open_tok):
+ return text.find(close_tok)
+ return text.find(close_tok) + find_closing(text[text.find(close_tok)+1:], open_tok, close_tok) + 1
+
+def unpretty_string(s):
+ s = s.strip()
+ while s.find(" ") != -1:
+ s = s.replace(" "," ")
+ while s.find("\t") != -1:
+ s = s.replace("\t"," ")
+ s = s.replace(" (","(")
+ return s
+
+class WEnum:
+ name = None
+ namespace = None
+ values = []
+
+ def from_string(str_def, namespace, line_number):
+ str_def = str_def.strip()
+ if not str.startswith(str_def, "enum "):
+ return None
+ if str_def.count(";") != 1:
+ return None
+ str_def = str_def[5:]
+ enum = WEnum()
+ split = str_def.split(":")
+ if(len(split) != 2):
+ return None
+ enum.name = split[0].strip()
+ if enum.name not in enum_names:
+ return None
+ str_def = split[1]
+ if str_def.count("{") != str_def.count("}") != 1:
+ return None
+ if len(str_def) < str_def.find("}")+2 or str_def[str_def.find("}")+1] != ';':
+ return None
+ str_def = str_def.split("{")[-1].split("}")[0]
+ enum.values = []
+ for val in str_def.split(','):
+ enum.values.append(val.strip().split('=')[0].strip())
+ enum.namespace = namespace
+ return enum
+
+ def gen_boost_py(self):
+ text = "\n\t\tenum_<" + self.namespace + "::" + self.name + ">(\"" + self.name + "\")\n"
+ for value in self.values:
+ text += "\t\t\t.value(\"" + value + "\"," + self.namespace + "::" + value + ")\n"
+ text += "\t\t\t;\n"
+ return text
+
+ def __str__(self):
+ ret = "Enum " + self.namespace + "::" + self.name + "(\n"
+ for val in self.values:
+ ret = ret + "\t" + val + "\n"
+ return ret + ")"
+
+ def __repr__(self):
+ return __str__(self)
+
+class WConstructor:
+ orig_text = None
+ args = []
+ containing_file = None
+ member_of = None
+ duplicate = False
+ protected = False
+
+ def __init__(self, containing_file, class_):
+ self.orig_text = "Auto generated default constructor"
+ self.args = []
+ self.containing_file = containing_file
+ self.member_of = class_
+ self.protected = False
+
+ def from_string(str_def, containing_file, class_, line_number, protected = False):
+ if class_ == None:
+ return None
+ if str_def.count("delete;") > 0:
+ return None
+ con = WConstructor(containing_file, class_)
+ con.orig_text = str_def
+ con.args = []
+ con.duplicate = False
+ con.protected = protected
+ if not str.startswith(str_def, class_.name + "("):
+ return None
+ str_def = str_def[len(class_.name)+1:]
+ found = find_closing(str_def, "(", ")")
+ if found == -1:
+ return None
+ str_def = str_def[0:found].strip()
+ if len(str_def) == 0:
+ return con
+ for arg in split_list(str_def, ","):
+ parsed = Attribute.from_string(arg.strip(), containing_file, line_number)
+ if parsed == None:
+ return None
+ con.args.append(parsed)
+ return con
+
+ def gen_decl(self):
+ if self.duplicate or self.protected:
+ return ""
+ text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+ text += "\n\t\t" + self.member_of.name + "("
+ for arg in self.args:
+ text += arg.gen_listitem() + ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ text += ");\n"
+ return text
+
+ def gen_decl_derive(self):
+ if self.duplicate or self.protected:
+ return ""
+ text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+ text += "\n\t\t" + self.member_of.name + "("
+ for arg in self.args:
+ text += arg.gen_listitem() + ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ text += ")"
+ if len(self.args) == 0:
+ return text + "{}"
+ text += " : "
+ text += self.member_of.namespace + "::" + self.member_of.name + "("
+ for arg in self.args:
+ text += arg.gen_call() + ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ text += "){}\n"
+ return text
+
+ def gen_decl_wrapperclass(self):
+ if self.duplicate or self.protected:
+ return ""
+ text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+ text += "\n\t\t" + self.member_of.name + "Wrap("
+ for arg in self.args:
+ text += arg.gen_listitem() + ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ text += ")"
+ if len(self.args) == 0:
+ return text + "{}"
+ text += " : "
+ text += self.member_of.name + "("
+ for arg in self.args:
+ text += arg.gen_call() + ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ text += "){}\n"
+ return text
+
+ def gen_decl_hash_py(self):
+ text = self.member_of.name + "("
+ for arg in self.args:
+ text += arg.gen_listitem_hash() + ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ text += ");"
+ return text
+
+ def gen_def(self):
+ if self.duplicate or self.protected:
+ return ""
+ text = "\n\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+ text += "\n\t" + self.member_of.name + "::" + self.member_of.name + "("
+ for arg in self.args:
+ text += arg.gen_listitem() + ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ text +=")\n\t{"
+ for arg in self.args:
+ text += arg.gen_translation()
+ if self.member_of.link_type != link_types.derive:
+ text += "\n\t\tthis->ref_obj = new " + self.member_of.namespace + "::" + self.member_of.name + "("
+ for arg in self.args:
+ text += arg.gen_call() + ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ if self.member_of.link_type != link_types.derive:
+ text += ");"
+ if self.member_of.link_type == link_types.global_list:
+ text += "\n\t\tthis->" + self.member_of.id_.varname + " = this->ref_obj->" + self.member_of.id_.varname + ";"
+ for arg in self.args:
+ text += arg.gen_cleanup()
+ text += "\n\t}\n"
+ return text
+
+ def gen_boost_py(self):
+ if self.duplicate or self.protected or len(self.args) == 0:
+ return ""
+ text = "\n\t\t\t.def(init"
+ text += "<"
+ for a in self.args:
+ text += a.gen_listitem_hash() + ", "
+ text = text[0:-2] + ">())"
+ return text
+
+class WFunction:
+ orig_text = None
+ is_static = False
+ is_inline = False
+ is_virtual = False
+ ret_attr_type = attr_types.default
+ is_operator = False
+ ret_type = None
+ name = None
+ alias = None
+ args = []
+ containing_file = None
+ member_of = None
+ duplicate = False
+ namespace = ""
+
+ def from_string(str_def, containing_file, class_, line_number, namespace):
+ if str_def.count("delete;") > 0:
+ return None
+ func = WFunction()
+ func.is_static = False
+ func.is_inline = False
+ func.is_virtual = False
+ func.ret_attr_type = attr_types.default
+ func.is_operator = False
+ func.member_of = None
+ func.orig_text = str_def
+ func.args = []
+ func.containing_file = containing_file
+ func.member_of = class_
+ func.duplicate = False
+ func.namespace = namespace
+ str_def = str_def.replace("operator ","operator")
+ if str.startswith(str_def, "static "):
+ func.is_static = True
+ str_def = str_def[7:]
+ else:
+ func.is_static = False
+ if str.startswith(str_def, "inline "):
+ func.is_inline = True
+ str_def = str_def[7:]
+ else:
+ func.is_inline = False
+ if str.startswith(str_def, "virtual "):
+ func.is_virtual = True
+ str_def = str_def[8:]
+ else:
+ func.is_virtual = False
+
+ if str_def.count(" ") == 0:
+ return None
+
+ parts = split_list(str_def.strip(), " ")
+
+ prefix = ""
+ i = 0
+ for part in parts:
+ if part in ["unsigned", "long", "short"]:
+ prefix += part + " "
+ i += 1
+ else:
+ break
+ parts = parts[i:]
+
+ if len(parts) <= 1:
+ return None
+
+ func.ret_type = WType.from_string(prefix + parts[0], containing_file, line_number)
+
+ if func.ret_type == None:
+ return None
+
+ str_def = parts[1]
+ for part in parts[2:]:
+ str_def = str_def + " " + part
+
+ found = str_def.find("(")
+ if found == -1 or (str_def.find(" ") != -1 and found > str_def.find(" ")):
+ return None
+ func.name = str_def[:found]
+ str_def = str_def[found:]
+ if func.name.find("operator") != -1 and str.startswith(str_def, "()("):
+ func.name += "()"
+ str_def = str_def[2:]
+ str_def = str_def[1:]
+ if func.name.find("operator") != -1:
+ func.is_operator = True
+ if func.name.find("*") == 0:
+ func.name = func.name.replace("*", "")
+ func.ret_type.attr_type = attr_types.star
+ if func.name.find("&&") == 0:
+ func.name = func.name.replace("&&", "")
+ func.ret_type.attr_type = attr_types.ampamp
+ if func.name.find("&") == 0:
+ func.name = func.name.replace("&", "")
+ func.ret_type.attr_type = attr_types.amp
+
+ found = find_closing(str_def, "(", ")")
+ if found == -1:
+ return None
+ str_def = str_def[0:found]
+ if func.name in blacklist_methods:
+ return None
+ if func.namespace != None and func.namespace != "":
+ if (func.namespace + "::" + func.name) in blacklist_methods:
+ return None
+ if func.member_of != None:
+ if (func.namespace + "::" + func.member_of.name + "::" + func.name) in blacklist_methods:
+ return None
+ if func.is_operator and func.name.replace(" ","").replace("operator","").split("::")[-1] not in wrappable_operators:
+ return None
+
+ testname = func.name
+ if func.is_operator:
+ testname = testname[:testname.find("operator")]
+ if testname.count(")") != 0 or testname.count("(") != 0 or testname.count("~") != 0 or testname.count(";") != 0 or testname.count(">") != 0 or testname.count("<") != 0 or testname.count("throw") != 0:
+ return None
+
+ func.alias = func.name
+ if func.name in keyword_aliases:
+ func.alias = keyword_aliases[func.name]
+ str_def = str_def[:found].strip()
+ if(len(str_def) == 0):
+ return func
+ for arg in split_list(str_def, ","):
+ if arg.strip() == "...":
+ continue
+ parsed = Attribute.from_string(arg.strip(), containing_file, line_number)
+ if parsed == None:
+ return None
+ func.args.append(parsed)
+ return func
+
+ def gen_alias(self):
+ self.alias = self.name
+ for arg in self.args:
+ self.alias += "__" + arg.wtype.gen_text_cpp().replace("::", "_").replace("<","_").replace(">","_").replace(" ","").replace("*","").replace(",","")
+
+ def gen_decl(self):
+ if self.duplicate:
+ return ""
+ text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+ text += "\n\t\t"
+ if self.is_static:
+ text += "static "
+ text += self.ret_type.gen_text() + " " + self.alias + "("
+ for arg in self.args:
+ text += arg.gen_listitem()
+ text += ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ text += ");\n"
+ return text
+
+ def gen_decl_virtual(self):
+ if self.duplicate:
+ return ""
+ if not self.is_virtual:
+ return self.gen_decl()
+ text = "\n\t\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+ text += "\n\t\tvirtual "
+ if self.is_static:
+ text += "static "
+ text += self.ret_type.gen_text() + " py_" + self.alias + "("
+ for arg in self.args:
+ text += arg.gen_listitem()
+ text += ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ text += ")"
+ if len(self.args) == 0:
+ text += "{}"
+ else:
+ text += "\n\t\t{"
+ for arg in self.args:
+ text += "\n\t\t\t(void)" + arg.gen_varname() + ";"
+ text += "\n\t\t}\n"
+ text += "\n\t\tvirtual "
+ if self.is_static:
+ text += "static "
+ text += self.ret_type.gen_text() + " " + self.name + "("
+ for arg in self.args:
+ text += arg.gen_listitem_cpp()
+ text += ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ text += ") YS_OVERRIDE;\n"
+ return text
+
+ def gen_decl_hash_py(self):
+ text = self.ret_type.gen_text() + " " + self.alias + "("
+ for arg in self.args:
+ text += arg.gen_listitem_hash() + ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ text += ");"
+ return text
+
+ def gen_def(self):
+ if self.duplicate:
+ return ""
+ text = "\n\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+ text += "\n\t" + self.ret_type.gen_text() + " "
+ if self.member_of != None:
+ text += self.member_of.name + "::"
+ text += self.alias + "("
+ for arg in self.args:
+ text += arg.gen_listitem()
+ text += ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ text +=")\n\t{"
+ for arg in self.args:
+ text += arg.gen_translation()
+ text += "\n\t\t"
+ if self.ret_type.name != "void":
+ if self.ret_type.name in known_containers:
+ text += self.ret_type.gen_text_cpp()
+ else:
+ text += self.ret_type.gen_text()
+ if self.ret_type.name in classnames or (self.ret_type.name in known_containers and self.ret_type.attr_type == attr_types.star):
+ text += "*"
+ text += " ret_ = "
+ if self.ret_type.name in classnames:
+ text += self.ret_type.name + "::get_py_obj("
+ if self.member_of == None:
+ text += "::" + self.namespace + "::" + self.alias + "("
+ elif self.is_static:
+ text += self.member_of.namespace + "::" + self.member_of.name + "::" + self.name + "("
+ else:
+ text += "this->get_cpp_obj()->" + self.name + "("
+ for arg in self.args:
+ text += arg.gen_call() + ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ if self.ret_type.name in classnames:
+ text += ")"
+ text += ");"
+ for arg in self.args:
+ text += arg.gen_cleanup()
+ if self.ret_type.name != "void":
+ if self.ret_type.name in classnames:
+ text += "\n\t\treturn *ret_;"
+ elif self.ret_type.name in known_containers:
+ text += known_containers[self.ret_type.name].translate_cpp("ret_", self.ret_type.cont.args, "\n\t\t", self.ret_type.attr_type == attr_types.star)
+ text += "\n\t\treturn ret____tmp;"
+ else:
+ text += "\n\t\treturn ret_;"
+ text += "\n\t}\n"
+ return text
+
+ def gen_def_virtual(self):
+ if self.duplicate:
+ return ""
+ if not self.is_virtual:
+ return self.gen_def()
+ text = "\n\t// WRAPPED from \"" + self.orig_text.replace("\n"," ") + "\" in " + self.containing_file
+ text += "\n\t"
+ if self.is_static:
+ text += "static "
+ text += self.ret_type.gen_text() + " " + self.member_of.name + "::" + self.name + "("
+ for arg in self.args:
+ text += arg.gen_listitem_cpp()
+ text += ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ text += ")\n\t{"
+ for arg in self.args:
+ text += arg.gen_translation_cpp()
+ text += "\n\t\t"
+ if self.member_of == None:
+ text += "::" + self.namespace + "::" + self.alias + "("
+ elif self.is_static:
+ text += self.member_of.namespace + "::" + self.member_of.name + "::" + self.name + "("
+ else:
+ text += "py_" + self.alias + "("
+ for arg in self.args:
+ text += arg.gen_call_cpp() + ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ if self.ret_type.name in classnames:
+ text += ")"
+ text += ");"
+ for arg in self.args:
+ text += arg.gen_cleanup()
+ text += "\n\t}\n"
+ return text
+
+ def gen_default_impl(self):
+ if self.duplicate:
+ return ""
+ if not self.is_virtual:
+ return ""
+ text = "\n\n\t\t" + self.ret_type.gen_text() + " py_" + self.alias + "("
+ for arg in self.args:
+ text += arg.gen_listitem() + ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+
+ call_string = "py_" + self.alias + "("
+ for arg in self.args:
+ call_string += arg.gen_varname() + ", "
+ if len(self.args) > 0:
+ call_string = call_string[0:-2]
+ call_string += ");"
+
+ text += ")\n\t\t{"
+ text += "\n\t\t\tif(boost::python::override py_" + self.alias + " = this->get_override(\"py_" + self.alias + "\"))"
+ text += "\n\t\t\t\t" + call_string
+ text += "\n\t\t\telse"
+ text += "\n\t\t\t\t" + self.member_of.name + "::" + call_string
+ text += "\n\t\t}"
+
+ text += "\n\n\t\t" + self.ret_type.gen_text() + " default_py_" + self.alias + "("
+ for arg in self.args:
+ text += arg.gen_listitem() + ", "
+ if len(self.args) > 0:
+ text = text[:-2]
+ text += ")\n\t\t{"
+ text += "\n\t\t\tthis->" + self.member_of.name + "::" + call_string
+ text += "\n\t\t}"
+ return text
+
+
+ def gen_boost_py(self):
+ if self.duplicate:
+ return ""
+ if self.member_of == None:
+ text = "\n\t\tdef"
+ else:
+ text = "\n\t\t\t.def"
+ if len(self.args) > -1:
+ if self.ret_type.name in known_containers:
+ text += "<" + known_containers[self.ret_type.name].typename + " "
+ else:
+ text += "<" + self.ret_type.name + " "
+ if self.member_of == None or self.is_static:
+ text += "(*)("
+ else:
+ text += "(" + self.member_of.name + "::*)("
+ for a in self.args:
+ text += a.gen_listitem_hash() + ", "
+ if len(self.args) > 0:
+ text = text[0:-2] + ")>"
+ else:
+ text += "void)>"
+
+ if self.is_operator:
+ text += "(\"" + wrappable_operators[self.name.replace("operator","")] + "\""
+ else:
+ if self.member_of != None and self.member_of.link_type == link_types.derive and self.is_virtual:
+ text += "(\"py_" + self.alias + "\""
+ else:
+ text += "(\"" + self.alias + "\""
+ if self.member_of != None:
+ text += ", &" + self.member_of.name + "::"
+ if self.member_of.link_type == link_types.derive and self.is_virtual:
+ text += "py_" + self.alias
+ text += ", &" + self.member_of.name + "Wrap::default_py_" + self.alias
+ else:
+ text += self.alias
+
+ text += ")"
+ else:
+ text += ", " + "YOSYS_PYTHON::" + self.alias + ");"
+ return text
+
+class WMember:
+ orig_text = None
+ wtype = attr_types.default
+ name = None
+ containing_file = None
+ member_of = None
+ namespace = ""
+ is_const = False
+
+ def from_string(str_def, containing_file, class_, line_number, namespace):
+ member = WMember()
+ member.orig_text = str_def
+ member.wtype = None
+ member.name = ""
+ member.containing_file = containing_file
+ member.member_of = class_
+ member.namespace = namespace
+ member.is_const = False
+
+ if str.startswith(str_def, "const "):
+ member.is_const = True
+ str_def = str_def[6:]
+
+ if str_def.count(" ") == 0:
+ return None
+
+ parts = split_list(str_def.strip(), " ")
+
+ prefix = ""
+ i = 0
+ for part in parts:
+ if part in ["unsigned", "long", "short"]:
+ prefix += part + " "
+ i += 1
+ else:
+ break
+ parts = parts[i:]
+
+ if len(parts) <= 1:
+ return None
+
+ member.wtype = WType.from_string(prefix + parts[0], containing_file, line_number)
+
+ if member.wtype == None:
+ return None
+
+ str_def = parts[1]
+ for part in parts[2:]:
+ str_def = str_def + " " + part
+
+ if str_def.find("(") != -1 or str_def.find(")") != -1 or str_def.find("{") != -1 or str_def.find("}") != -1:
+ return None
+
+ found = str_def.find(";")
+ if found == -1:
+ return None
+
+ found_eq = str_def.find("=")
+ if found_eq != -1:
+ found = found_eq
+
+ member.name = str_def[:found]
+ str_def = str_def[found+1:]
+ if member.name.find("*") == 0:
+ member.name = member.name.replace("*", "")
+ member.wtype.attr_type = attr_types.star
+ if member.name.find("&&") == 0:
+ member.name = member.name.replace("&&", "")
+ member.wtype.attr_type = attr_types.ampamp
+ if member.name.find("&") == 0:
+ member.name = member.name.replace("&", "")
+ member.wtype.attr_type = attr_types.amp
+
+ if(len(str_def.strip()) != 0):
+ return None
+
+ if len(member.name.split(",")) > 1:
+ member_list = []
+ for name in member.name.split(","):
+ name = name.strip();
+ member_list.append(WMember())
+ member_list[-1].orig_text = member.orig_text
+ member_list[-1].wtype = member.wtype
+ member_list[-1].name = name
+ member_list[-1].containing_file = member.containing_file
+ member_list[-1].member_of = member.member_of
+ member_list[-1].namespace = member.namespace
+ member_list[-1].is_const = member.is_const
+ return member_list
+
+ return member
+
+ def gen_decl(self):
+ text = "\n\t\t" + self.wtype.gen_text() + " get_var_py_" + self.name + "();\n"
+ if self.is_const:
+ return text
+ if self.wtype.name in classnames:
+ text += "\n\t\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " *rhs);\n"
+ else:
+ text += "\n\t\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " rhs);\n"
+ return text
+
+ def gen_def(self):
+ text = "\n\t" + self.wtype.gen_text() + " " + self.member_of.name +"::get_var_py_" + self.name + "()"
+ text += "\n\t{\n\t\t"
+ if self.wtype.attr_type == attr_types.star:
+ text += "if(this->get_cpp_obj()->" + self.name + " == NULL)\n\t\t\t"
+ text += "throw std::runtime_error(\"Member \\\"" + self.name + "\\\" is NULL\");\n\t\t"
+ if self.wtype.name in known_containers:
+ text += self.wtype.gen_text_cpp()
+ else:
+ text += self.wtype.gen_text()
+
+ if self.wtype.name in classnames or (self.wtype.name in known_containers and self.wtype.attr_type == attr_types.star):
+ text += "*"
+ text += " ret_ = "
+ if self.wtype.name in classnames:
+ text += self.wtype.name + "::get_py_obj("
+ if self.wtype.attr_type != attr_types.star:
+ text += "&"
+ text += "this->get_cpp_obj()->" + self.name
+ if self.wtype.name in classnames:
+ text += ")"
+ text += ";"
+
+ if self.wtype.name in classnames:
+ text += "\n\t\treturn *ret_;"
+ elif self.wtype.name in known_containers:
+ text += known_containers[self.wtype.name].translate_cpp("ret_", self.wtype.cont.args, "\n\t\t", self.wtype.attr_type == attr_types.star)
+ text += "\n\t\treturn ret____tmp;"
+ else:
+ text += "\n\t\treturn ret_;"
+ text += "\n\t}\n"
+
+ if self.is_const:
+ return text
+
+ ret = Attribute(self.wtype, "rhs");
+
+ if self.wtype.name in classnames:
+ text += "\n\tvoid " + self.member_of.name+ "::set_var_py_" + self.name + "(" + self.wtype.gen_text() + " *rhs)"
+ else:
+ text += "\n\tvoid " + self.member_of.name+ "::set_var_py_" + self.name + "(" + self.wtype.gen_text() + " rhs)"
+ text += "\n\t{"
+ text += ret.gen_translation()
+ text += "\n\t\tthis->get_cpp_obj()->" + self.name + " = " + ret.gen_call() + ";"
+ text += "\n\t}\n"
+
+ return text;
+
+ def gen_boost_py(self):
+ text = "\n\t\t\t.add_property(\"" + self.name + "\", &" + self.member_of.name + "::get_var_py_" + self.name
+ if not self.is_const:
+ text += ", &" + self.member_of.name + "::set_var_py_" + self.name
+ text += ")"
+ return text
+
+def concat_namespace(tuple_list):
+ if len(tuple_list) == 0:
+ return ""
+ ret = ""
+ for namespace in tuple_list:
+ ret += "::" + namespace[0]
+ return ret[2:]
+
+def calc_ident(text):
+ if len(text) == 0 or text[0] != ' ':
+ return 0
+ return calc_ident(text[1:]) + 1
+
+def assure_length(text, length, left = False):
+ if len(text) > length:
+ return text[:length]
+ if left:
+ return text + " "*(length - len(text))
+ return " "*(length - len(text)) + text
+
+def parse_header(source):
+ debug("Parsing " + source.name + ".pyh",1)
+ source_file = open(source.name + ".pyh", "r")
+
+ source_text = []
+ in_line = source_file.readline()
+
+ namespaces = []
+
+ while(in_line):
+ if(len(in_line)>1):
+ source_text.append(in_line.replace("char *", "char_p ").replace("char* ", "char_p "))
+ in_line = source_file.readline()
+
+ i = 0
+
+ namespaces = []
+ class_ = None
+ private_segment = False
+
+ while i < len(source_text):
+ line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", " namespace YOSYS_NAMESPACE{").replace("YOSYS_NAMESPACE_END"," }")
+ ugly_line = unpretty_string(line)
+
+ if str.startswith(ugly_line, "namespace "):# and ugly_line.find("std") == -1 and ugly_line.find("__") == -1:
+ namespace_name = ugly_line[10:].replace("{","").strip()
+ namespaces.append((namespace_name, ugly_line.count("{")))
+ debug("-----NAMESPACE " + concat_namespace(namespaces) + "-----",3)
+ i += 1
+ continue
+
+ if len(namespaces) != 0:
+ namespaces[-1] = (namespaces[-1][0], namespaces[-1][1] + ugly_line.count("{") - ugly_line.count("}"))
+ if namespaces[-1][1] == 0:
+ debug("-----END NAMESPACE " + concat_namespace(namespaces) + "-----",3)
+ del namespaces[-1]
+ i += 1
+ continue
+
+ if class_ == None and (str.startswith(ugly_line, "struct ") or str.startswith(ugly_line, "class")) and ugly_line.count(";") == 0:
+
+ struct_name = ugly_line.split(" ")[1].split("::")[-1]
+ impl_namespaces = ugly_line.split(" ")[1].split("::")[:-1]
+ complete_namespace = concat_namespace(namespaces)
+ for namespace in impl_namespaces:
+ complete_namespace += "::" + namespace
+ debug("\tFound " + struct_name + " in " + complete_namespace,2)
+ class_ = (class_by_name(struct_name), ugly_line.count("{"))#calc_ident(line))
+ if struct_name in classnames:
+ class_[0].namespace = complete_namespace
+ i += 1
+ continue
+
+ if class_ != None:
+ class_ = (class_[0], class_[1] + ugly_line.count("{") - ugly_line.count("}"))
+ if class_[1] == 0:
+ if class_[0] == None:
+ debug("\tExiting unknown class", 3)
+ else:
+ debug("\tExiting class " + class_[0].name, 3)
+ class_ = None
+ private_segment = False
+ i += 1
+ continue
+
+ if class_ != None and (line.find("private:") != -1 or line.find("protected:") != -1):
+ private_segment = True
+ i += 1
+ continue
+ if class_ != None and line.find("public:") != -1:
+ private_segment = False
+ i += 1
+ continue
+
+ candidate = None
+
+ if private_segment and class_ != None and class_[0] != None:
+ candidate = WConstructor.from_string(ugly_line, source.name, class_[0], i, True)
+ if candidate != None:
+ debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
+ class_[0].found_constrs.append(candidate)
+ i += 1
+ continue
+
+ if not private_segment and (class_ == None or class_[0] != None):
+ if class_ != None:
+ candidate = WFunction.from_string(ugly_line, source.name, class_[0], i, concat_namespace(namespaces))
+ else:
+ candidate = WFunction.from_string(ugly_line, source.name, None, i, concat_namespace(namespaces))
+ if candidate != None and candidate.name.find("::") == -1:
+ if class_ == None:
+ debug("\tFound unowned function \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2)
+ unowned_functions.append(candidate)
+ else:
+ debug("\t\tFound function \"" + candidate.name + "\" of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
+ class_[0].found_funs.append(candidate)
+ else:
+ candidate = WEnum.from_string(ugly_line, concat_namespace(namespaces), i)
+ if candidate != None:
+ enums.append(candidate)
+ debug("\tFound enum \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2)
+ elif class_ != None and class_[1] == 1:
+ candidate = WConstructor.from_string(ugly_line, source.name, class_[0], i)
+ if candidate != None:
+ debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
+ class_[0].found_constrs.append(candidate)
+ else:
+ candidate = WMember.from_string(ugly_line, source.name, class_[0], i, concat_namespace(namespaces))
+ if candidate != None:
+ if type(candidate) == list:
+ for c in candidate:
+ debug("\t\tFound member \"" + c.name + "\" of class \"" + class_[0].name + "\" of type \"" + c.wtype.name + "\"", 2)
+ class_[0].found_vars.extend(candidate)
+ else:
+ debug("\t\tFound member \"" + candidate.name + "\" of class \"" + class_[0].name + "\" of type \"" + candidate.wtype.name + "\"", 2)
+ class_[0].found_vars.append(candidate)
+
+ j = i
+ line = unpretty_string(line)
+ while candidate == None and j+1 < len(source_text) and line.count(';') <= 1 and line.count("(") >= line.count(")"):
+ j += 1
+ line = line + "\n" + unpretty_string(source_text[j])
+ if class_ != None:
+ candidate = WFunction.from_string(ugly_line, source.name, class_[0], i, concat_namespace(namespaces))
+ else:
+ candidate = WFunction.from_string(ugly_line, source.name, None, i, concat_namespace(namespaces))
+ if candidate != None and candidate.name.find("::") == -1:
+ if class_ == None:
+ debug("\tFound unowned function \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2)
+ unowned_functions.append(candidate)
+ else:
+ debug("\t\tFound function \"" + candidate.name + "\" of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
+ class_[0].found_funs.append(candidate)
+ continue
+ candidate = WEnum.from_string(line, concat_namespace(namespaces), i)
+ if candidate != None:
+ enums.append(candidate)
+ debug("\tFound enum \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces),2)
+ continue
+ if class_ != None:
+ candidate = WConstructor.from_string(line, source.name, class_[0], i)
+ if candidate != None:
+ debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
+ class_[0].found_constrs.append(candidate)
+ continue
+ if candidate != None:
+ while i < j:
+ i += 1
+ line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", " namespace YOSYS_NAMESPACE{").replace("YOSYS_NAMESPACE_END"," }")
+ ugly_line = unpretty_string(line)
+ if len(namespaces) != 0:
+ namespaces[-1] = (namespaces[-1][0], namespaces[-1][1] + ugly_line.count("{") - ugly_line.count("}"))
+ if namespaces[-1][1] == 0:
+ debug("-----END NAMESPACE " + concat_namespace(namespaces) + "-----",3)
+ del namespaces[-1]
+ if class_ != None:
+ class_ = (class_[0] , class_[1] + ugly_line.count("{") - ugly_line.count("}"))
+ if class_[1] == 0:
+ if class_[0] == None:
+ debug("\tExiting unknown class", 3)
+ else:
+ debug("\tExiting class " + class_[0].name, 3)
+ class_ = None
+ private_segment = False
+ i += 1
+ else:
+ i += 1
+
+def debug(message, level):
+ if level <= debug.debug_level:
+ print(message)
+
+def expand_function(f):
+ fun_list = []
+ arg_list = []
+ for arg in f.args:
+ if arg.default_value != None and (arg.wtype.name.split(" ")[-1] in primitive_types or arg.wtype.name in enum_names or (arg.wtype.name in classnames and arg.default_value == "nullptr")):
+ fi = copy.deepcopy(f)
+ fi.args = copy.deepcopy(arg_list)
+ fun_list.append(fi)
+ arg_list.append(arg)
+ fun_list.append(f)
+ return fun_list
+
+def expand_functions():
+ global unowned_functions
+ new_funs = []
+ for fun in unowned_functions:
+ new_funs.extend(expand_function(fun))
+ unowned_functions = new_funs
+ for source in sources:
+ for class_ in source.classes:
+ new_funs = []
+ for fun in class_.found_funs:
+ new_funs.extend(expand_function(fun))
+ class_.found_funs = new_funs
+
+def clean_duplicates():
+ for source in sources:
+ for class_ in source.classes:
+ known_decls = {}
+ for fun in class_.found_funs:
+ if fun.gen_decl_hash_py() in known_decls:
+ debug("Multiple declarations of " + fun.gen_decl_hash_py(),3)
+ other = known_decls[fun.gen_decl_hash_py()]
+ other.gen_alias()
+ fun.gen_alias()
+ if fun.gen_decl_hash_py() == other.gen_decl_hash_py():
+ fun.duplicate = True
+ debug("Disabled \"" + fun.gen_decl_hash_py() + "\"", 3)
+ else:
+ known_decls[fun.gen_decl_hash_py()] = fun
+ known_decls = []
+ for con in class_.found_constrs:
+ if con.gen_decl_hash_py() in known_decls:
+ debug("Multiple declarations of " + con.gen_decl_hash_py(),3)
+ con.duplicate = True
+ else:
+ known_decls.append(con.gen_decl_hash_py())
+ known_decls = []
+ for fun in unowned_functions:
+ if fun.gen_decl_hash_py() in known_decls:
+ debug("Multiple declarations of " + fun.gen_decl_hash_py(),3)
+ fun.duplicate = True
+ else:
+ known_decls.append(fun.gen_decl_hash_py())
+
+def gen_wrappers(filename, debug_level_ = 0):
+ debug.debug_level = debug_level_
+ for source in sources:
+ parse_header(source)
+
+ expand_functions()
+ clean_duplicates()
+
+ import shutil
+ import math
+ col = shutil.get_terminal_size((80,20)).columns
+ debug("-"*col, 1)
+ debug("-"*math.floor((col-7)/2)+"SUMMARY"+"-"*math.ceil((col-7)/2), 1)
+ debug("-"*col, 1)
+ for source in sources:
+ for class_ in source.classes:
+ debug("Class " + assure_length(class_.name, len(max(classnames, key=len)), True) + " contains " + assure_length(str(len(class_.found_vars)), 3, False) + " member variables, "+ assure_length(str(len(class_.found_funs)), 3, False) + " methods and " + assure_length(str(len(class_.found_constrs)), 2, False) + " constructors", 1)
+ if len(class_.found_constrs) == 0:
+ class_.found_constrs.append(WConstructor(source.name, class_))
+ debug(str(len(unowned_functions)) + " functions are unowned", 1)
+ for enum in enums:
+ debug("Enum " + assure_length(enum.name, len(max(enum_names, key=len)), True) + " contains " + assure_length(str(len(enum.values)), 2, False) + " values", 1)
+ debug("-"*col, 1)
+ wrapper_file = open(filename, "w+")
+ wrapper_file.write(
+"""/*
+ * 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.
+ *
+ * This is a generated file and can be overwritten by make
+ */
+
+#ifdef WITH_PYTHON
+""")
+ for source in sources:
+ wrapper_file.write("#include \""+source.name+".h\"\n")
+ wrapper_file.write("""
+#include <boost/python/module.hpp>
+#include <boost/python/class.hpp>
+#include <boost/python/wrapper.hpp>
+#include <boost/python/call.hpp>
+#include <boost/python.hpp>
+
+USING_YOSYS_NAMESPACE
+
+namespace YOSYS_PYTHON {
+""")
+
+ for source in sources:
+ for wclass in source.classes:
+ wrapper_file.write("\n\tstruct " + wclass.name + ";")
+
+ wrapper_file.write("\n")
+
+ for source in sources:
+ for wclass in source.classes:
+ wrapper_file.write(wclass.gen_decl(source.name))
+
+ wrapper_file.write("\n")
+
+ for source in sources:
+ for wclass in source.classes:
+ wrapper_file.write(wclass.gen_funs(source.name))
+
+ for fun in unowned_functions:
+ wrapper_file.write(fun.gen_def())
+
+ wrapper_file.write(""" struct Initializer
+ {
+ Initializer() {
+ if(!Yosys::yosys_already_setup())
+ {
+ Yosys::log_streams.push_back(&std::cout);
+ Yosys::log_error_stderr = true;
+ Yosys::yosys_setup();
+ }
+ }
+
+ Initializer(Initializer const &) {}
+
+ ~Initializer() {
+ Yosys::yosys_shutdown();
+ }
+ };
+
+ BOOST_PYTHON_MODULE(libyosys)
+ {
+ using namespace boost::python;
+
+ class_<Initializer>("Initializer");
+ scope().attr("_hidden") = new Initializer();
+""")
+
+ for enum in enums:
+ wrapper_file.write(enum.gen_boost_py())
+
+ for source in sources:
+ for wclass in source.classes:
+ wrapper_file.write(wclass.gen_boost_py())
+
+ for fun in unowned_functions:
+ wrapper_file.write(fun.gen_boost_py())
+
+ wrapper_file.write("\n\t}\n}\n#endif")
+
+def print_includes():
+ for source in sources:
+ print(source.name + ".pyh")
diff --git a/passes/cmds/add.cc b/passes/cmds/add.cc
index cfccca96..af6f7043 100644
--- a/passes/cmds/add.cc
+++ b/passes/cmds/add.cc
@@ -71,7 +71,7 @@ static void add_wire(RTLIL::Design *design, RTLIL::Module *module, std::string n
RTLIL::Module *mod = design->modules_.at(it.second->type);
if (!design->selected_whole_module(mod->name))
continue;
- if (mod->get_bool_attribute("\\blackbox"))
+ if (mod->get_blackbox_attribute())
continue;
if (it.second->hasPort(name))
continue;
diff --git a/passes/cmds/blackbox.cc b/passes/cmds/blackbox.cc
index 6094f8f1..d09ed872 100644
--- a/passes/cmds/blackbox.cc
+++ b/passes/cmds/blackbox.cc
@@ -23,7 +23,7 @@ USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct BlackboxPass : public Pass {
- BlackboxPass() : Pass("blackbox", "change type of cells in the design") { }
+ BlackboxPass() : Pass("blackbox", "convert modules into blackbox modules") { }
void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc
index 606276e6..038ab7c7 100644
--- a/passes/cmds/bugpoint.cc
+++ b/passes/cmds/bugpoint.cc
@@ -128,7 +128,7 @@ struct BugpointPass : public Pass {
{
for (auto &it : design_copy->modules_)
{
- if (it.second->get_bool_attribute("\\blackbox"))
+ if (it.second->get_blackbox_attribute())
continue;
if (index++ == seed)
@@ -143,7 +143,7 @@ struct BugpointPass : public Pass {
{
for (auto mod : design_copy->modules())
{
- if (mod->get_bool_attribute("\\blackbox"))
+ if (mod->get_blackbox_attribute())
continue;
for (auto wire : mod->wires())
@@ -168,7 +168,7 @@ struct BugpointPass : public Pass {
{
for (auto mod : design_copy->modules())
{
- if (mod->get_bool_attribute("\\blackbox"))
+ if (mod->get_blackbox_attribute())
continue;
for (auto &it : mod->cells_)
@@ -186,7 +186,7 @@ struct BugpointPass : public Pass {
{
for (auto mod : design_copy->modules())
{
- if (mod->get_bool_attribute("\\blackbox"))
+ if (mod->get_blackbox_attribute())
continue;
for (auto cell : mod->cells())
@@ -281,6 +281,9 @@ struct BugpointPass : public Pass {
}
extra_args(args, argidx, design);
+ if (script.empty())
+ log_cmd_error("Missing -script option.\n");
+
if (!has_part)
{
modules = true;
@@ -298,7 +301,7 @@ struct BugpointPass : public Pass {
if (!check_logfile(grep))
log_cmd_error("The provided grep string is not found in the log file!\n");
- int seed = 0, crashing_seed = seed;
+ int seed = 0;
bool found_something = false, stage2 = false;
while (true)
{
@@ -324,7 +327,6 @@ struct BugpointPass : public Pass {
if (crashing_design != design)
delete crashing_design;
crashing_design = simplified;
- crashing_seed = seed;
found_something = true;
}
else
diff --git a/passes/cmds/cover.cc b/passes/cmds/cover.cc
index 0ec74767..1128116b 100644
--- a/passes/cmds/cover.cc
+++ b/passes/cmds/cover.cc
@@ -98,21 +98,23 @@ struct CoverPass : public Pass {
}
if ((args[argidx] == "-o" || args[argidx] == "-a" || args[argidx] == "-d") && argidx+1 < args.size()) {
const char *open_mode = args[argidx] == "-a" ? "a+" : "w";
- std::string filename = args[++argidx];
+ const std::string &filename = args[++argidx];
+ FILE *f = nullptr;
if (args[argidx-1] == "-d") {
#ifdef _WIN32
log_cmd_error("The 'cover -d' option is not supported on win32.\n");
#else
char filename_buffer[4096];
snprintf(filename_buffer, 4096, "%s/yosys_cover_%d_XXXXXX.txt", filename.c_str(), getpid());
- filename = mkstemps(filename_buffer, 4);
+ f = fdopen(mkstemps(filename_buffer, 4), "w");
#endif
+ } else {
+ f = fopen(filename.c_str(), open_mode);
}
- FILE *f = fopen(filename.c_str(), open_mode);
if (f == NULL) {
for (auto f : out_files)
fclose(f);
- log_cmd_error("Can't create file %s.\n", args[argidx].c_str());
+ log_cmd_error("Can't create file %s%s.\n", args[argidx-1] == "-d" ? "in directory " : "", args[argidx].c_str());
}
out_files.push_back(f);
continue;
diff --git a/passes/cmds/plugin.cc b/passes/cmds/plugin.cc
index aa6d5b6c..4c16b56c 100644
--- a/passes/cmds/plugin.cc
+++ b/passes/cmds/plugin.cc
@@ -23,9 +23,18 @@
# include <dlfcn.h>
#endif
+#ifdef WITH_PYTHON
+# include <boost/algorithm/string/predicate.hpp>
+# include <Python.h>
+# include <boost/filesystem.hpp>
+#endif
+
YOSYS_NAMESPACE_BEGIN
std::map<std::string, void*> loaded_plugins;
+#ifdef WITH_PYTHON
+std::map<std::string, void*> loaded_python_plugins;
+#endif
std::map<std::string, std::string> loaded_plugin_aliases;
#ifdef YOSYS_ENABLE_PLUGINS
@@ -36,7 +45,35 @@ void load_plugin(std::string filename, std::vector<std::string> aliases)
if (filename.find('/') == std::string::npos)
filename = "./" + filename;
+ #ifdef WITH_PYTHON
+ if (!loaded_plugins.count(filename) && !loaded_python_plugins.count(filename)) {
+ #else
if (!loaded_plugins.count(filename)) {
+ #endif
+
+ #ifdef WITH_PYTHON
+
+ boost::filesystem::path full_path(filename);
+
+ if(strcmp(full_path.extension().c_str(), ".py") == 0)
+ {
+ std::string path(full_path.parent_path().c_str());
+ filename = full_path.filename().c_str();
+ filename = filename.substr(0,filename.size()-3);
+ PyRun_SimpleString(("sys.path.insert(0,\""+path+"\")").c_str());
+ PyErr_Print();
+ PyObject *module_p = PyImport_ImportModule(filename.c_str());
+ if(module_p == NULL)
+ {
+ PyErr_Print();
+ log_cmd_error("Can't load python module `%s'\n", full_path.filename().c_str());
+ return;
+ }
+ loaded_python_plugins[orig_filename] = module_p;
+ Pass::init_register();
+ } else {
+ #endif
+
void *hdl = dlopen(filename.c_str(), RTLD_LAZY|RTLD_LOCAL);
if (hdl == NULL && orig_filename.find('/') == std::string::npos)
hdl = dlopen((proc_share_dirname() + "plugins/" + orig_filename + ".so").c_str(), RTLD_LAZY|RTLD_LOCAL);
@@ -44,6 +81,10 @@ void load_plugin(std::string filename, std::vector<std::string> aliases)
log_cmd_error("Can't load module `%s': %s\n", filename.c_str(), dlerror());
loaded_plugins[orig_filename] = hdl;
Pass::init_register();
+
+ #ifdef WITH_PYTHON
+ }
+ #endif
}
for (auto &alias : aliases)
@@ -107,7 +148,11 @@ struct PluginPass : public Pass {
if (list_mode)
{
log("\n");
+#ifdef WITH_PYTHON
+ if (loaded_plugins.empty() and loaded_python_plugins.empty())
+#else
if (loaded_plugins.empty())
+#endif
log("No plugins loaded.\n");
else
log("Loaded plugins:\n");
@@ -115,6 +160,11 @@ struct PluginPass : public Pass {
for (auto &it : loaded_plugins)
log(" %s\n", it.first.c_str());
+#ifdef WITH_PYTHON
+ for (auto &it : loaded_python_plugins)
+ log(" %s\n", it.first.c_str());
+#endif
+
if (!loaded_plugin_aliases.empty()) {
log("\n");
int max_alias_len = 1;
diff --git a/passes/cmds/qwp.cc b/passes/cmds/qwp.cc
index 1c64a7b7..adbe89e3 100644
--- a/passes/cmds/qwp.cc
+++ b/passes/cmds/qwp.cc
@@ -291,7 +291,7 @@ struct QwpWorker
// gaussian elimination
for (int i = 0; i < N; i++)
{
- if (config.verbose && ((i+1) % (N/15)) == 0)
+ if (config.verbose && N > 15 && ((i+1) % (N/15)) == 0)
log("> Solved %d%%: %d/%d\n", (100*(i+1))/N, i+1, N);
// find best row
diff --git a/passes/cmds/setattr.cc b/passes/cmds/setattr.cc
index d38a6b3d..b9fcc3e7 100644
--- a/passes/cmds/setattr.cc
+++ b/passes/cmds/setattr.cc
@@ -128,6 +128,45 @@ struct SetattrPass : public Pass {
}
} SetattrPass;
+struct WbflipPass : public Pass {
+ WbflipPass() : Pass("wbflip", "flip the whitebox attribute") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" wbflip [selection]\n");
+ log("\n");
+ log("Flip the whitebox attribute on selected cells. I.e. if it's set, unset it, and\n");
+ log("vice-versa. Blackbox cells are not effected by this command.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ // if (arg == "-mod") {
+ // flag_mod = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (Module *module : design->modules())
+ {
+ if (!design->selected(module))
+ continue;
+
+ if (module->get_bool_attribute("\\blackbox"))
+ continue;
+
+ module->set_bool_attribute("\\whitebox", !module->get_bool_attribute("\\whitebox"));
+ }
+ }
+} WbflipPass;
+
struct SetparamPass : public Pass {
SetparamPass() : Pass("setparam", "set/unset parameters on objects") { }
void help() YS_OVERRIDE
diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc
index f6949c82..3eedc86b 100644
--- a/passes/cmds/setundef.cc
+++ b/passes/cmds/setundef.cc
@@ -393,44 +393,112 @@ struct SetundefPass : public Pass {
ffbits.insert(bit);
}
- for (auto wire : module->wires())
+ auto process_initwires = [&]()
{
- if (!wire->attributes.count("\\init"))
- continue;
+ dict<Wire*, int> wire_weights;
- for (auto bit : sigmap(wire))
- ffbits.erase(bit);
+ for (auto wire : initwires)
+ {
+ int weight = 0;
- initwires.insert(wire);
- }
+ for (auto bit : sigmap(wire))
+ weight += ffbits.count(bit) ? +1 : -1;
+
+ wire_weights[wire] = weight;
+ }
+
+ initwires.sort([&](Wire *a, Wire *b) { return wire_weights.at(a) > wire_weights.at(b); });
+
+ for (auto wire : initwires)
+ {
+ Const &initval = wire->attributes["\\init"];
+ initval.bits.resize(GetSize(wire), State::Sx);
+
+ for (int i = 0; i < GetSize(wire); i++) {
+ SigBit bit = sigmap(SigBit(wire, i));
+ if (initval[i] == State::Sx && ffbits.count(bit)) {
+ initval[i] = worker.next_bit();
+ ffbits.erase(bit);
+ }
+ }
+
+ if (initval.is_fully_undef())
+ wire->attributes.erase("\\init");
+ }
+
+ initwires.clear();
+ };
for (int wire_types = 0; wire_types < 2; wire_types++)
- for (auto wire : module->wires())
+ {
+ // prioritize wires that already have an init attribute
+ if (!ffbits.empty())
{
- if (wire->name[0] == (wire_types ? '\\' : '$'))
- next_wire:
- continue;
+ for (auto wire : module->wires())
+ {
+ if (wire->name[0] == (wire_types ? '\\' : '$'))
+ continue;
- for (auto bit : sigmap(wire))
- if (!ffbits.count(bit))
- goto next_wire;
+ if (!wire->attributes.count("\\init"))
+ continue;
- for (auto bit : sigmap(wire))
- ffbits.erase(bit);
+ Const &initval = wire->attributes["\\init"];
+ initval.bits.resize(GetSize(wire), State::Sx);
+
+ if (initval.is_fully_undef()) {
+ wire->attributes.erase("\\init");
+ continue;
+ }
- initwires.insert(wire);
+ for (int i = 0; i < GetSize(wire); i++)
+ if (initval[i] != State::Sx)
+ ffbits.erase(sigmap(SigBit(wire, i)));
+
+ initwires.insert(wire);
+ }
+
+ process_initwires();
}
- for (auto wire : initwires)
- {
- Const &initval = wire->attributes["\\init"];
+ // next consider wires that completely contain bits to be initialized
+ if (!ffbits.empty())
+ {
+ for (auto wire : module->wires())
+ {
+ if (wire->name[0] == (wire_types ? '\\' : '$'))
+ continue;
- for (int i = 0; i < GetSize(wire); i++)
- if (GetSize(initval) <= i)
- initval.bits.push_back(worker.next_bit());
- else if (initval.bits[i] == State::Sx)
- initval.bits[i] = worker.next_bit();
+ for (auto bit : sigmap(wire))
+ if (!ffbits.count(bit))
+ goto next_wire;
+
+ initwires.insert(wire);
+
+ next_wire:
+ continue;
+ }
+
+ process_initwires();
+ }
+
+ // finally use whatever wire we can find.
+ if (!ffbits.empty())
+ {
+ for (auto wire : module->wires())
+ {
+ if (wire->name[0] == (wire_types ? '\\' : '$'))
+ continue;
+
+ for (auto bit : sigmap(wire))
+ if (ffbits.count(bit))
+ initwires.insert(wire);
+ }
+
+ process_initwires();
+ }
}
+
+ log_assert(ffbits.empty());
}
module->rewrite_sigspecs(worker);
diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc
index 58acd302..cf729215 100644
--- a/passes/cmds/show.cc
+++ b/passes/cmds/show.cc
@@ -237,15 +237,34 @@ struct ShowWorker
int idx = single_idx_count++;
for (int rep, i = int(sig.chunks().size())-1; i >= 0; i -= rep) {
const RTLIL::SigChunk &c = sig.chunks().at(i);
- net = gen_signode_simple(c, false);
- log_assert(!net.empty());
+ if (!driver && c.wire == nullptr) {
+ RTLIL::State s1 = c.data.front();
+ for (auto s2 : c.data)
+ if (s1 != s2)
+ goto not_const_stream;
+ net.clear();
+ } else {
+ not_const_stream:
+ net = gen_signode_simple(c, false);
+ log_assert(!net.empty());
+ }
for (rep = 1; i-rep >= 0 && c == sig.chunks().at(i-rep); rep++) {}
std::string repinfo = rep > 1 ? stringf("%dx ", rep) : "";
if (driver) {
+ log_assert(!net.empty());
label_string += stringf("<s%d> %d:%d - %s%d:%d |", i, pos, pos-c.width+1, repinfo.c_str(), c.offset+c.width-1, c.offset);
net_conn_map[net].in.insert(stringf("x%d:s%d", idx, i));
net_conn_map[net].bits = rep*c.width;
net_conn_map[net].color = nextColor(c, net_conn_map[net].color);
+ } else
+ if (net.empty()) {
+ log_assert(rep == 1);
+ label_string += stringf("%c -&gt; %d:%d |",
+ c.data.front() == State::S0 ? '0' :
+ c.data.front() == State::S1 ? '1' :
+ c.data.front() == State::Sx ? 'X' :
+ c.data.front() == State::Sz ? 'Z' : '?',
+ pos, pos-rep*c.width+1);
} else {
label_string += stringf("<s%d> %s%d:%d - %d:%d |", i, repinfo.c_str(), c.offset+c.width-1, c.offset, pos, pos-rep*c.width+1);
net_conn_map[net].out.insert(stringf("x%d:s%d", idx, i));
@@ -555,7 +574,7 @@ struct ShowWorker
if (!design->selected_module(module->name))
continue;
if (design->selected_whole_module(module->name)) {
- if (module->get_bool_attribute("\\blackbox")) {
+ if (module->get_blackbox_attribute()) {
// log("Skipping blackbox module %s.\n", id2cstr(module->name));
continue;
} else
@@ -771,7 +790,7 @@ struct ShowPass : public Pass {
if (format != "ps" && format != "dot") {
int modcount = 0;
for (auto &mod_it : design->modules_) {
- if (mod_it.second->get_bool_attribute("\\blackbox"))
+ if (mod_it.second->get_blackbox_attribute())
continue;
if (mod_it.second->cells_.empty() && mod_it.second->connections().empty())
continue;
diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc
index 54f4ea81..27c5fb60 100644
--- a/passes/cmds/stat.cc
+++ b/passes/cmds/stat.cc
@@ -37,7 +37,9 @@ struct statdata_t
STAT_INT_MEMBERS
#undef X
double area;
+ string tech;
+ std::map<RTLIL::IdString, int> techinfo;
std::map<RTLIL::IdString, int, RTLIL::sort_by_id_str> num_cells_by_type;
std::set<RTLIL::IdString> unknown_cell_area;
@@ -70,8 +72,10 @@ struct statdata_t
#undef X
}
- statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area)
+ statdata_t(RTLIL::Design *design, RTLIL::Module *mod, bool width_mode, const dict<IdString, double> &cell_area, string techname)
{
+ tech = techname;
+
#define X(_name) _name = 0;
STAT_NUMERIC_MEMBERS
#undef X
@@ -153,7 +157,8 @@ struct statdata_t
log(" Number of processes: %6d\n", num_processes);
log(" Number of cells: %6d\n", num_cells);
for (auto &it : num_cells_by_type)
- log(" %-26s %6d\n", RTLIL::id2cstr(it.first), it.second);
+ if (it.second)
+ log(" %-26s %6d\n", RTLIL::id2cstr(it.first), it.second);
if (!unknown_cell_area.empty()) {
log("\n");
@@ -165,6 +170,59 @@ struct statdata_t
log("\n");
log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), area);
}
+
+ if (tech == "xilinx")
+ {
+ int lut6_cnt = num_cells_by_type["\\LUT6"];
+ int lut5_cnt = num_cells_by_type["\\LUT5"];
+ int lut4_cnt = num_cells_by_type["\\LUT4"];
+ int lut3_cnt = num_cells_by_type["\\LUT3"];
+ int lut2_cnt = num_cells_by_type["\\LUT2"];
+ int lut1_cnt = num_cells_by_type["\\LUT1"];
+ int lc_cnt = 0;
+
+ lc_cnt += lut6_cnt;
+
+ lc_cnt += lut5_cnt;
+ if (lut1_cnt) {
+ int cnt = std::min(lut5_cnt, lut1_cnt);
+ lut5_cnt -= cnt;
+ lut1_cnt -= cnt;
+ }
+
+ lc_cnt += lut4_cnt;
+ if (lut1_cnt) {
+ int cnt = std::min(lut4_cnt, lut1_cnt);
+ lut4_cnt -= cnt;
+ lut1_cnt -= cnt;
+ }
+ if (lut2_cnt) {
+ int cnt = std::min(lut4_cnt, lut2_cnt);
+ lut4_cnt -= cnt;
+ lut2_cnt -= cnt;
+ }
+
+ lc_cnt += lut3_cnt;
+ if (lut1_cnt) {
+ int cnt = std::min(lut3_cnt, lut1_cnt);
+ lut3_cnt -= cnt;
+ lut1_cnt -= cnt;
+ }
+ if (lut2_cnt) {
+ int cnt = std::min(lut3_cnt, lut2_cnt);
+ lut3_cnt -= cnt;
+ lut2_cnt -= cnt;
+ }
+ if (lut3_cnt) {
+ int cnt = (lut3_cnt + 1) / 2;
+ lut3_cnt -= cnt;
+ }
+
+ lc_cnt += (lut2_cnt + lut1_cnt + 1) / 2;
+
+ log("\n");
+ log(" Estimated number of LCs: %10d\n", lc_cnt);
+ }
}
};
@@ -226,6 +284,10 @@ struct StatPass : public Pass {
log(" -liberty <liberty_file>\n");
log(" use cell area information from the provided liberty file\n");
log("\n");
+ log(" -tech <technology>\n");
+ log(" print area estemate for the specified technology. Currently supported\n");
+ log(" values for <technology>: xilinx\n");
+ log("\n");
log(" -width\n");
log(" annotate internal cell types with their word width.\n");
log(" e.g. $add_8 for an 8 bit wide $add cell.\n");
@@ -239,6 +301,7 @@ struct StatPass : public Pass {
RTLIL::Module *top_mod = NULL;
std::map<RTLIL::IdString, statdata_t> mod_stat;
dict<IdString, double> cell_area;
+ string techname;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@@ -253,6 +316,10 @@ struct StatPass : public Pass {
read_liberty_cellarea(cell_area, liberty_file);
continue;
}
+ if (args[argidx] == "-tech" && argidx+1 < args.size()) {
+ techname = args[++argidx];
+ continue;
+ }
if (args[argidx] == "-top" && argidx+1 < args.size()) {
if (design->modules_.count(RTLIL::escape_id(args[argidx+1])) == 0)
log_cmd_error("Can't find module %s.\n", args[argidx+1].c_str());
@@ -263,13 +330,16 @@ struct StatPass : public Pass {
}
extra_args(args, argidx, design);
+ if (techname != "" && techname != "xilinx")
+ log_cmd_error("Unsupported technology: '%s'\n", techname.c_str());
+
for (auto mod : design->selected_modules())
{
if (!top_mod && design->full_selection())
if (mod->get_bool_attribute("\\top"))
top_mod = mod;
- statdata_t data(design, mod, width_mode, cell_area);
+ statdata_t data(design, mod, width_mode, cell_area, techname);
mod_stat[mod->name] = data;
log("\n");
diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc
index ee96ace8..1a44bdae 100644
--- a/passes/cmds/tee.cc
+++ b/passes/cmds/tee.cc
@@ -52,7 +52,9 @@ struct TeePass : public Pass {
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::vector<FILE*> backup_log_files, files_to_close;
+ std::vector<std::ostream*> backup_log_streams;
int backup_log_verbose_level = log_verbose_level;
+ backup_log_streams = log_streams;
backup_log_files = log_files;
size_t argidx;
@@ -60,6 +62,7 @@ struct TeePass : public Pass {
{
if (args[argidx] == "-q" && files_to_close.empty()) {
log_files.clear();
+ log_streams.clear();
continue;
}
if ((args[argidx] == "-o" || args[argidx] == "-a") && argidx+1 < args.size()) {
@@ -89,6 +92,7 @@ struct TeePass : public Pass {
for (auto cf : files_to_close)
fclose(cf);
log_files = backup_log_files;
+ log_streams = backup_log_streams;
throw;
}
@@ -97,6 +101,7 @@ struct TeePass : public Pass {
log_verbose_level = backup_log_verbose_level;
log_files = backup_log_files;
+ log_streams = backup_log_streams;
}
} TeePass;
diff --git a/passes/cmds/trace.cc b/passes/cmds/trace.cc
index f5305cde..cf3e46ac 100644
--- a/passes/cmds/trace.cc
+++ b/passes/cmds/trace.cc
@@ -94,4 +94,38 @@ struct TracePass : public Pass {
}
} TracePass;
+struct DebugPass : public Pass {
+ DebugPass() : Pass("debug", "run command with debug log messages enabled") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" debug cmd\n");
+ log("\n");
+ log("Execute the specified command with debug log messages enabled\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // .. parse options ..
+ break;
+ }
+
+ log_force_debug++;
+
+ try {
+ std::vector<std::string> new_args(args.begin() + argidx, args.end());
+ Pass::call(design, new_args);
+ } catch (...) {
+ log_force_debug--;
+ throw;
+ }
+
+ log_force_debug--;
+ }
+} DebugPass;
+
PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/write_file.cc b/passes/cmds/write_file.cc
index 9613b462..64a762d7 100644
--- a/passes/cmds/write_file.cc
+++ b/passes/cmds/write_file.cc
@@ -62,7 +62,7 @@ struct WriteFileFrontend : public Frontend {
if (argidx < args.size() && args[argidx].rfind("-", 0) != 0)
output_filename = args[argidx++];
else
- log_cmd_error("Missing putput filename.\n");
+ log_cmd_error("Missing output filename.\n");
extra_args(f, filename, args, argidx);
diff --git a/passes/equiv/equiv_opt.cc b/passes/equiv/equiv_opt.cc
index 86550a69..3596dfd7 100644
--- a/passes/equiv/equiv_opt.cc
+++ b/passes/equiv/equiv_opt.cc
@@ -44,7 +44,10 @@ struct EquivOptPass:public ScriptPass
log(" useful for handling architecture-specific primitives.\n");
log("\n");
log(" -assert\n");
- log(" produce an error if the circuits are not equivalent\n");
+ log(" produce an error if the circuits are not equivalent.\n");
+ log("\n");
+ log(" -undef\n");
+ log(" enable modelling of undef states during equiv_induct.\n");
log("\n");
log("The following commands are executed by this verification command:\n");
help_script();
@@ -52,13 +55,14 @@ struct EquivOptPass:public ScriptPass
}
std::string command, techmap_opts;
- bool assert;
+ bool assert, undef;
void clear_flags() YS_OVERRIDE
{
command = "";
techmap_opts = "";
assert = false;
+ undef = false;
}
void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE
@@ -84,6 +88,10 @@ struct EquivOptPass:public ScriptPass
assert = true;
continue;
}
+ if (args[argidx] == "-undef") {
+ undef = true;
+ continue;
+ }
break;
}
@@ -134,12 +142,17 @@ struct EquivOptPass:public ScriptPass
opts = " -map <filename> ...";
else
opts = techmap_opts;
- run("techmap -D EQUIV -autoproc" + opts);
+ run("techmap -wb -D EQUIV -autoproc" + opts);
}
if (check_label("prove")) {
run("equiv_make gold gate equiv");
- run("equiv_induct equiv");
+ if (help_mode)
+ run("equiv_induct [-undef] equiv");
+ else if (undef)
+ run("equiv_induct -undef equiv");
+ else
+ run("equiv_induct equiv");
if (help_mode)
run("equiv_status [-assert] equiv");
else if (assert)
diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc
index 88c339e8..213437c0 100644
--- a/passes/hierarchy/hierarchy.cc
+++ b/passes/hierarchy/hierarchy.cc
@@ -346,9 +346,9 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
}
RTLIL::Module *mod = design->modules_[cell->type];
- if (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) {
+ if (design->modules_.at(cell->type)->get_blackbox_attribute()) {
if (flag_simcheck)
- log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox module.\n",
+ log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox/whitebox module.\n",
cell->type.c_str(), module->name.c_str(), cell->name.c_str());
continue;
}
@@ -451,7 +451,7 @@ void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::
if (indent == 0)
log("Top module: %s\n", mod->name.c_str());
- else if (!mod->get_bool_attribute("\\blackbox"))
+ else if (!mod->get_blackbox_attribute())
log("Used module: %*s%s\n", indent, "", mod->name.c_str());
used.insert(mod);
@@ -491,7 +491,7 @@ void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib)
int del_counter = 0;
for (auto mod : del_modules) {
- if (!purge_lib && mod->get_bool_attribute("\\blackbox"))
+ if (!purge_lib && mod->get_blackbox_attribute())
continue;
log("Removing unused module `%s'.\n", mod->name.c_str());
design->modules_.erase(mod->name);
@@ -562,7 +562,8 @@ struct HierarchyPass : public Pass {
log("In parametric designs, a module might exists in several variations with\n");
log("different parameter values. This pass looks at all modules in the current\n");
log("design an re-runs the language frontends for the parametric modules as\n");
- log("needed.\n");
+ log("needed. It also resolves assignments to wired logic data types (wand/wor),\n");
+ log("resolves positional module parameters, unroll array instances, and more.\n");
log("\n");
log(" -check\n");
log(" also check the design hierarchy. this generates an error when\n");
@@ -570,7 +571,7 @@ struct HierarchyPass : public Pass {
log("\n");
log(" -simcheck\n");
log(" like -check, but also throw an error if blackbox modules are\n");
- log(" instantiated, and throw an error if the design has no top module\n");
+ log(" instantiated, and throw an error if the design has no top module.\n");
log("\n");
log(" -purge_lib\n");
log(" by default the hierarchy command will not remove library (blackbox)\n");
@@ -583,20 +584,23 @@ struct HierarchyPass : public Pass {
log("\n");
log(" -keep_positionals\n");
log(" per default this pass also converts positional arguments in cells\n");
- log(" to arguments using port names. this option disables this behavior.\n");
+ log(" to arguments using port names. This option disables this behavior.\n");
log("\n");
log(" -keep_portwidths\n");
log(" per default this pass adjusts the port width on cells that are\n");
- log(" module instances when the width does not match the module port. this\n");
+ log(" module instances when the width does not match the module port. This\n");
log(" option disables this behavior.\n");
log("\n");
+ log(" -nodefaults\n");
+ log(" do not resolve input port default values\n");
+ log("\n");
log(" -nokeep_asserts\n");
log(" per default this pass sets the \"keep\" attribute on all modules\n");
- log(" that directly or indirectly contain one or more $assert cells. this\n");
- log(" option disables this behavior.\n");
+ log(" that directly or indirectly contain one or more formal properties.\n");
+ log(" This option disables this behavior.\n");
log("\n");
log(" -top <module>\n");
- log(" use the specified top module to built a design hierarchy. modules\n");
+ log(" use the specified top module to build the design hierarchy. Modules\n");
log(" outside this tree (unused modules) are removed.\n");
log("\n");
log(" when the -top option is used, the 'top' attribute will be set on the\n");
@@ -606,6 +610,12 @@ struct HierarchyPass : public Pass {
log(" -auto-top\n");
log(" automatically determine the top of the design hierarchy and mark it.\n");
log("\n");
+ log(" -chparam name value \n");
+ log(" elaborate the top module using this parameter value. Modules on which\n");
+ log(" this parameter does not exist may cause a warning message to be output.\n");
+ log(" This option can be specified multiple times to override multiple\n");
+ log(" parameters. String values must be passed in double quotes (\").\n");
+ log("\n");
log("In -generate mode this pass generates blackbox modules for the given cell\n");
log("types (wildcards supported). For this the design is searched for cells that\n");
log("match the given types and then the given port declarations are used to\n");
@@ -638,9 +648,11 @@ struct HierarchyPass : public Pass {
bool generate_mode = false;
bool keep_positionals = false;
bool keep_portwidths = false;
+ bool nodefaults = false;
bool nokeep_asserts = false;
std::vector<std::string> generate_cells;
std::vector<generate_port_decl_t> generate_ports;
+ std::map<std::string, std::string> parameters;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@@ -704,6 +716,10 @@ struct HierarchyPass : public Pass {
keep_portwidths = true;
continue;
}
+ if (args[argidx] == "-nodefaults") {
+ nodefaults = true;
+ continue;
+ }
if (args[argidx] == "-nokeep_asserts") {
nokeep_asserts = true;
continue;
@@ -715,28 +731,61 @@ struct HierarchyPass : public Pass {
if (args[argidx] == "-top") {
if (++argidx >= args.size())
log_cmd_error("Option -top requires an additional argument!\n");
- top_mod = design->modules_.count(RTLIL::escape_id(args[argidx])) ? design->modules_.at(RTLIL::escape_id(args[argidx])) : NULL;
- if (top_mod == NULL && design->modules_.count("$abstract" + RTLIL::escape_id(args[argidx]))) {
- dict<RTLIL::IdString, RTLIL::Const> empty_parameters;
- design->modules_.at("$abstract" + RTLIL::escape_id(args[argidx]))->derive(design, empty_parameters);
- top_mod = design->modules_.count(RTLIL::escape_id(args[argidx])) ? design->modules_.at(RTLIL::escape_id(args[argidx])) : NULL;
- }
- if (top_mod == NULL)
- load_top_mod = args[argidx];
+ load_top_mod = args[argidx];
continue;
}
if (args[argidx] == "-auto-top") {
auto_top_mode = true;
continue;
}
+ if (args[argidx] == "-chparam" && argidx+2 < args.size()) {
+ const std::string &key = args[++argidx];
+ const std::string &value = args[++argidx];
+ auto r = parameters.emplace(key, value);
+ if (!r.second) {
+ log_warning("-chparam %s already specified: overwriting.\n", key.c_str());
+ r.first->second = value;
+ }
+ continue;
+ }
break;
}
extra_args(args, argidx, design, false);
- if (!load_top_mod.empty()) {
+ if (!load_top_mod.empty())
+ {
+ IdString top_name = RTLIL::escape_id(load_top_mod);
+ IdString abstract_id = "$abstract" + RTLIL::escape_id(load_top_mod);
+ top_mod = design->module(top_name);
+
+ dict<RTLIL::IdString, RTLIL::Const> top_parameters;
+ for (auto &para : parameters) {
+ SigSpec sig_value;
+ if (!RTLIL::SigSpec::parse(sig_value, NULL, para.second))
+ log_cmd_error("Can't decode value '%s'!\n", para.second.c_str());
+ top_parameters[RTLIL::escape_id(para.first)] = sig_value.as_const();
+ }
+
+ if (top_mod == nullptr && design->module(abstract_id))
+ top_mod = design->module(design->module(abstract_id)->derive(design, top_parameters));
+ else if (top_mod != nullptr && !top_parameters.empty())
+ top_mod = design->module(top_mod->derive(design, top_parameters));
+
+ if (top_mod != nullptr && top_mod->name != top_name) {
+ Module *m = top_mod->clone();
+ m->name = top_name;
+ Module *old_mod = design->module(top_name);
+ if (old_mod)
+ design->remove(old_mod);
+ design->add(m);
+ top_mod = m;
+ }
+ }
+
+ if (top_mod == nullptr && !load_top_mod.empty()) {
#ifdef YOSYS_ENABLE_VERIFIC
if (verific_import_pending) {
- verific_import(design, load_top_mod);
+ verific_import(design, parameters, load_top_mod);
top_mod = design->module(RTLIL::escape_id(load_top_mod));
}
#endif
@@ -745,7 +794,7 @@ struct HierarchyPass : public Pass {
} else {
#ifdef YOSYS_ENABLE_VERIFIC
if (verific_import_pending)
- verific_import(design);
+ verific_import(design, parameters);
#endif
}
@@ -846,7 +895,7 @@ struct HierarchyPass : public Pass {
std::map<RTLIL::Module*, bool> cache;
for (auto mod : design->modules())
if (set_keep_assert(cache, mod)) {
- log("Module %s directly or indirectly contains $assert cells -> setting \"keep\" attribute.\n", log_id(mod));
+ log("Module %s directly or indirectly contains formal properties -> setting \"keep\" attribute.\n", log_id(mod));
mod->set_bool_attribute("\\keep");
}
}
@@ -899,66 +948,212 @@ struct HierarchyPass : public Pass {
}
}
+ if (!nodefaults)
+ {
+ dict<IdString, dict<IdString, Const>> defaults_db;
+
+ for (auto module : design->modules())
+ for (auto wire : module->wires())
+ if (wire->port_input && wire->attributes.count("\\defaultvalue"))
+ defaults_db[module->name][wire->name] = wire->attributes.at("\\defaultvalue");
+
+ for (auto module : design->modules())
+ for (auto cell : module->cells())
+ {
+ if (defaults_db.count(cell->type) == 0)
+ continue;
+
+ if (keep_positionals) {
+ bool found_positionals = false;
+ for (auto &conn : cell->connections())
+ if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9')
+ found_positionals = true;
+ if (found_positionals)
+ continue;
+ }
+
+ for (auto &it : defaults_db.at(cell->type))
+ if (!cell->hasPort(it.first))
+ cell->setPort(it.first, it.second);
+ }
+ }
+
std::set<Module*> blackbox_derivatives;
std::vector<Module*> design_modules = design->modules();
for (auto module : design_modules)
- for (auto cell : module->cells())
{
- Module *m = design->module(cell->type);
+ pool<Wire*> wand_wor_index;
+ dict<Wire*, SigSpec> wand_map, wor_map;
+ vector<SigSig> new_connections;
- if (m == nullptr)
- continue;
+ for (auto wire : module->wires())
+ {
+ if (wire->get_bool_attribute("\\wand")) {
+ wand_map[wire] = SigSpec();
+ wand_wor_index.insert(wire);
+ }
+ if (wire->get_bool_attribute("\\wor")) {
+ wor_map[wire] = SigSpec();
+ wand_wor_index.insert(wire);
+ }
+ }
+
+ for (auto &conn : module->connections())
+ {
+ SigSig new_conn;
+ int cursor = 0;
+
+ for (auto c : conn.first.chunks())
+ {
+ Wire *w = c.wire;
+ SigSpec rhs = conn.second.extract(cursor, GetSize(c));
+
+ if (wand_wor_index.count(w) == 0) {
+ new_conn.first.append(c);
+ new_conn.second.append(rhs);
+ } else {
+ if (wand_map.count(w)) {
+ SigSpec sig = SigSpec(State::S1, GetSize(w));
+ sig.replace(c.offset, rhs);
+ wand_map.at(w).append(sig);
+ } else {
+ SigSpec sig = SigSpec(State::S0, GetSize(w));
+ sig.replace(c.offset, rhs);
+ wor_map.at(w).append(sig);
+ }
+ }
+ cursor += GetSize(c);
+ }
+ new_connections.push_back(new_conn);
+ }
+ module->new_connections(new_connections);
- if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) {
- IdString new_m_name = m->derive(design, cell->parameters, true);
- if (new_m_name.empty())
+ for (auto cell : module->cells())
+ {
+ if (!cell->known())
continue;
- if (new_m_name != m->name) {
- m = design->module(new_m_name);
- blackbox_derivatives.insert(m);
+
+ for (auto &conn : cell->connections())
+ {
+ if (!cell->output(conn.first))
+ continue;
+
+ SigSpec new_sig;
+ bool update_port = false;
+
+ for (auto c : conn.second.chunks())
+ {
+ Wire *w = c.wire;
+
+ if (wand_wor_index.count(w) == 0) {
+ new_sig.append(c);
+ continue;
+ }
+
+ Wire *t = module->addWire(NEW_ID, GetSize(c));
+ new_sig.append(t);
+ update_port = true;
+
+ if (wand_map.count(w)) {
+ SigSpec sig = SigSpec(State::S1, GetSize(w));
+ sig.replace(c.offset, t);
+ wand_map.at(w).append(sig);
+ } else {
+ SigSpec sig = SigSpec(State::S0, GetSize(w));
+ sig.replace(c.offset, t);
+ wor_map.at(w).append(sig);
+ }
+ }
+
+ if (update_port)
+ cell->setPort(conn.first, new_sig);
}
}
- for (auto &conn : cell->connections())
+ for (auto w : wand_wor_index)
{
- Wire *w = m->wire(conn.first);
+ bool wand = wand_map.count(w);
+ SigSpec sigs = wand ? wand_map.at(w) : wor_map.at(w);
- if (w == nullptr || w->port_id == 0)
+ if (GetSize(sigs) == 0)
continue;
- if (GetSize(conn.second) == 0)
+ if (GetSize(w) == 1) {
+ if (wand)
+ module->addReduceAnd(NEW_ID, sigs, w);
+ else
+ module->addReduceOr(NEW_ID, sigs, w);
continue;
+ }
- SigSpec sig = conn.second;
+ SigSpec s = sigs.extract(0, GetSize(w));
+ for (int i = GetSize(w); i < GetSize(sigs); i += GetSize(w)) {
+ if (wand)
+ s = module->And(NEW_ID, s, sigs.extract(i, GetSize(w)));
+ else
+ s = module->Or(NEW_ID, s, sigs.extract(i, GetSize(w)));
+ }
+ module->connect(w, s);
+ }
- if (!keep_portwidths && GetSize(w) != GetSize(conn.second))
- {
- if (GetSize(w) < GetSize(conn.second))
- {
- int n = GetSize(conn.second) - GetSize(w);
- if (!w->port_input && w->port_output)
- module->connect(sig.extract(GetSize(w), n), Const(0, n));
- sig.remove(GetSize(w), n);
+ for (auto cell : module->cells())
+ {
+ Module *m = design->module(cell->type);
+
+ if (m == nullptr)
+ continue;
+
+ if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) {
+ IdString new_m_name = m->derive(design, cell->parameters, true);
+ if (new_m_name.empty())
+ continue;
+ if (new_m_name != m->name) {
+ m = design->module(new_m_name);
+ blackbox_derivatives.insert(m);
}
- else
+ }
+
+ for (auto &conn : cell->connections())
+ {
+ Wire *w = m->wire(conn.first);
+
+ if (w == nullptr || w->port_id == 0)
+ continue;
+
+ if (GetSize(conn.second) == 0)
+ continue;
+
+ SigSpec sig = conn.second;
+
+ if (!keep_portwidths && GetSize(w) != GetSize(conn.second))
{
- int n = GetSize(w) - GetSize(conn.second);
- if (w->port_input && !w->port_output)
- sig.append(Const(0, n));
+ if (GetSize(w) < GetSize(conn.second))
+ {
+ int n = GetSize(conn.second) - GetSize(w);
+ if (!w->port_input && w->port_output)
+ module->connect(sig.extract(GetSize(w), n), Const(0, n));
+ sig.remove(GetSize(w), n);
+ }
else
- sig.append(module->addWire(NEW_ID, n));
+ {
+ int n = GetSize(w) - GetSize(conn.second);
+ if (w->port_input && !w->port_output)
+ sig.append(Const(0, n));
+ else
+ sig.append(module->addWire(NEW_ID, n));
+ }
+
+ if (!conn.second.is_fully_const() || !w->port_input || w->port_output)
+ log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell),
+ log_id(conn.first), GetSize(conn.second), GetSize(sig));
+ cell->setPort(conn.first, sig);
}
- if (!conn.second.is_fully_const() || !w->port_input || w->port_output)
- log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell),
- log_id(conn.first), GetSize(conn.second), GetSize(sig));
- cell->setPort(conn.first, sig);
+ if (w->port_output && !w->port_input && sig.has_const())
+ log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n",
+ log_id(module), log_id(cell), log_id(conn.first), log_id(cell->type), log_signal(sig));
}
-
- if (w->port_output && !w->port_input && sig.has_const())
- log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n",
- log_id(module), log_id(cell), log_id(conn.first), log_id(cell->type), log_signal(sig));
}
}
diff --git a/passes/hierarchy/uniquify.cc b/passes/hierarchy/uniquify.cc
index e6154e94..ad322091 100644
--- a/passes/hierarchy/uniquify.cc
+++ b/passes/hierarchy/uniquify.cc
@@ -75,7 +75,7 @@ struct UniquifyPass : public Pass {
if (tmod == nullptr)
continue;
- if (tmod->get_bool_attribute("\\blackbox"))
+ if (tmod->get_blackbox_attribute())
continue;
if (tmod->get_bool_attribute("\\unique") && newname == tmod->name)
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc
index 85ed1c05..ddc56d9b 100644
--- a/passes/memory/memory_bram.cc
+++ b/passes/memory/memory_bram.cc
@@ -744,7 +744,8 @@ grow_read_ports:;
if (clken) {
clock_domains[pi.clocks] = clkdom;
clock_polarities[pi.clkpol] = clkdom.second;
- read_transp[pi.transp] = transp;
+ if (!pi.make_transp)
+ read_transp[pi.transp] = transp;
pi.sig_clock = clkdom.first;
pi.sig_en = rd_en[cell_port_i];
pi.effective_clkpol = clkdom.second;
@@ -957,6 +958,8 @@ grow_read_ports:;
SigSpec addr_ok_q = addr_ok;
if ((pi.clocks || pi.make_outreg) && !addr_ok.empty()) {
addr_ok_q = module->addWire(NEW_ID);
+ if (!pi.sig_en.empty())
+ addr_ok = module->Mux(NEW_ID, addr_ok_q, addr_ok, pi.sig_en);
module->addDff(NEW_ID, pi.sig_clock, addr_ok, addr_ok_q, pi.effective_clkpol);
}
diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc
index 220d2929..32b97f27 100644
--- a/passes/memory/memory_dff.cc
+++ b/passes/memory/memory_dff.cc
@@ -17,6 +17,7 @@
*
*/
+#include <algorithm>
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
@@ -182,20 +183,27 @@ struct MemoryDffWorker
if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data))
{
- bool enable_invert = mux_cells_a.count(sig_data) != 0;
- Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data);
- SigSpec check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A"));
+ RTLIL::SigSpec en;
+ std::vector<RTLIL::SigSpec> check_q;
+
+ do {
+ bool enable_invert = mux_cells_a.count(sig_data) != 0;
+ Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data);
+ check_q.push_back(sigmap(mux->getPort(enable_invert ? "\\B" : "\\A")));
+ sig_data = sigmap(mux->getPort("\\Y"));
+ en.append(enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S"));
+ } while (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data));
- sig_data = sigmap(mux->getPort("\\Y"));
for (auto bit : sig_data)
if (sigbit_users_count[bit] > 1)
goto skip_ff_after_read_merging;
- if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && sig_data == check_q)
+ if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) &&
+ std::all_of(check_q.begin(), check_q.end(), [&](const SigSpec &cq) {return cq == sig_data; }))
{
disconnect_dff(sig_data);
cell->setPort("\\CLK", clk_data);
- cell->setPort("\\EN", enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S"));
+ cell->setPort("\\EN", en.size() > 1 ? module->ReduceAnd(NEW_ID, en) : en);
cell->setPort("\\DATA", sig_data);
cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc
index c3e0a2a4..ea364633 100644
--- a/passes/opt/Makefile.inc
+++ b/passes/opt/Makefile.inc
@@ -13,5 +13,7 @@ OBJS += passes/opt/wreduce.o
OBJS += passes/opt/opt_demorgan.o
OBJS += passes/opt/rmports.o
OBJS += passes/opt/opt_lut.o
+OBJS += passes/opt/pmux2shiftx.o
+OBJS += passes/opt/muxpack.o
endif
diff --git a/passes/opt/muxpack.cc b/passes/opt/muxpack.cc
new file mode 100644
index 00000000..6697d6ca
--- /dev/null
+++ b/passes/opt/muxpack.cc
@@ -0,0 +1,368 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 2019 Eddie Hung <eddie@fpgeh.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 "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct ExclusiveDatabase
+{
+ Module *module;
+ const SigMap &sigmap;
+
+ dict<SigBit, std::pair<SigSpec,std::vector<Const>>> sig_cmp_prev;
+
+ ExclusiveDatabase(Module *module, const SigMap &sigmap) : module(module), sigmap(sigmap)
+ {
+ SigSpec const_sig, nonconst_sig;
+ SigBit y_port;
+ pool<Cell*> reduce_or;
+ for (auto cell : module->cells()) {
+ if (cell->type == "$eq") {
+ nonconst_sig = sigmap(cell->getPort("\\A"));
+ const_sig = sigmap(cell->getPort("\\B"));
+ if (!const_sig.is_fully_const()) {
+ if (!nonconst_sig.is_fully_const())
+ continue;
+ std::swap(nonconst_sig, const_sig);
+ }
+ y_port = sigmap(cell->getPort("\\Y"));
+ }
+ else if (cell->type == "$logic_not") {
+ nonconst_sig = sigmap(cell->getPort("\\A"));
+ const_sig = Const(RTLIL::S0, GetSize(nonconst_sig));
+ y_port = sigmap(cell->getPort("\\Y"));
+ }
+ else if (cell->type == "$reduce_or") {
+ reduce_or.insert(cell);
+ continue;
+ }
+ else continue;
+
+ log_assert(!nonconst_sig.empty());
+ log_assert(!const_sig.empty());
+ sig_cmp_prev[y_port] = std::make_pair(nonconst_sig,std::vector<Const>{const_sig.as_const()});
+ }
+
+ for (auto cell : reduce_or) {
+ nonconst_sig = SigSpec();
+ std::vector<Const> values;
+ SigSpec a_port = sigmap(cell->getPort("\\A"));
+ for (auto bit : a_port) {
+ auto it = sig_cmp_prev.find(bit);
+ if (it == sig_cmp_prev.end()) {
+ nonconst_sig = SigSpec();
+ break;
+ }
+ if (nonconst_sig.empty())
+ nonconst_sig = it->second.first;
+ else if (nonconst_sig != it->second.first) {
+ nonconst_sig = SigSpec();
+ break;
+ }
+ for (auto value : it->second.second)
+ values.push_back(value);
+ }
+ if (nonconst_sig.empty())
+ continue;
+ y_port = sigmap(cell->getPort("\\Y"));
+ sig_cmp_prev[y_port] = std::make_pair(nonconst_sig,std::move(values));
+ }
+ }
+
+ bool query(const SigSpec &sig) const
+ {
+ SigSpec nonconst_sig;
+ pool<Const> const_values;
+
+ for (auto bit : sig.bits()) {
+ auto it = sig_cmp_prev.find(bit);
+ if (it == sig_cmp_prev.end())
+ return false;
+
+ if (nonconst_sig.empty())
+ nonconst_sig = it->second.first;
+ else if (nonconst_sig != it->second.first)
+ return false;
+
+ for (auto value : it->second.second)
+ if (!const_values.insert(value).second)
+ return false;
+ }
+
+ return true;
+ }
+};
+
+
+struct MuxpackWorker
+{
+ Module *module;
+ SigMap sigmap;
+
+ int mux_count, pmux_count;
+
+ pool<Cell*> remove_cells;
+
+ dict<SigSpec, Cell*> sig_chain_next;
+ dict<SigSpec, Cell*> sig_chain_prev;
+ pool<SigBit> sigbit_with_non_chain_users;
+ pool<Cell*> chain_start_cells;
+ pool<Cell*> candidate_cells;
+
+ ExclusiveDatabase excl_db;
+
+ void make_sig_chain_next_prev()
+ {
+ for (auto wire : module->wires())
+ {
+ if (wire->port_output || wire->get_bool_attribute("\\keep")) {
+ for (auto bit : sigmap(wire))
+ sigbit_with_non_chain_users.insert(bit);
+ }
+ }
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type.in("$mux", "$pmux") && !cell->get_bool_attribute("\\keep"))
+ {
+ SigSpec a_sig = sigmap(cell->getPort("\\A"));
+ SigSpec b_sig;
+ if (cell->type == "$mux")
+ b_sig = sigmap(cell->getPort("\\B"));
+ SigSpec y_sig = sigmap(cell->getPort("\\Y"));
+
+ if (sig_chain_next.count(a_sig))
+ for (auto a_bit : a_sig.bits())
+ sigbit_with_non_chain_users.insert(a_bit);
+ else {
+ sig_chain_next[a_sig] = cell;
+ candidate_cells.insert(cell);
+ }
+
+ if (!b_sig.empty()) {
+ if (sig_chain_next.count(b_sig))
+ for (auto b_bit : b_sig.bits())
+ sigbit_with_non_chain_users.insert(b_bit);
+ else {
+ sig_chain_next[b_sig] = cell;
+ candidate_cells.insert(cell);
+ }
+ }
+
+ sig_chain_prev[y_sig] = cell;
+ continue;
+ }
+
+ for (auto conn : cell->connections())
+ if (cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ sigbit_with_non_chain_users.insert(bit);
+ }
+ }
+
+ void find_chain_start_cells()
+ {
+ for (auto cell : candidate_cells)
+ {
+ log_debug("Considering %s (%s)\n", log_id(cell), log_id(cell->type));
+
+ SigSpec a_sig = sigmap(cell->getPort("\\A"));
+ if (cell->type == "$mux") {
+ SigSpec b_sig = sigmap(cell->getPort("\\B"));
+ if (sig_chain_prev.count(a_sig) + sig_chain_prev.count(b_sig) != 1)
+ goto start_cell;
+
+ if (!sig_chain_prev.count(a_sig))
+ a_sig = b_sig;
+ }
+ else if (cell->type == "$pmux") {
+ if (!sig_chain_prev.count(a_sig))
+ goto start_cell;
+ }
+ else log_abort();
+
+ for (auto bit : a_sig.bits())
+ if (sigbit_with_non_chain_users.count(bit))
+ goto start_cell;
+
+ {
+ Cell *prev_cell = sig_chain_prev.at(a_sig);
+ log_assert(prev_cell);
+ SigSpec s_sig = sigmap(cell->getPort("\\S"));
+ s_sig.append(sigmap(prev_cell->getPort("\\S")));
+ if (!excl_db.query(s_sig))
+ goto start_cell;
+ }
+
+ continue;
+
+ start_cell:
+ chain_start_cells.insert(cell);
+ }
+ }
+
+ vector<Cell*> create_chain(Cell *start_cell)
+ {
+ vector<Cell*> chain;
+
+ Cell *c = start_cell;
+ while (c != nullptr)
+ {
+ chain.push_back(c);
+
+ SigSpec y_sig = sigmap(c->getPort("\\Y"));
+
+ if (sig_chain_next.count(y_sig) == 0)
+ break;
+
+ c = sig_chain_next.at(y_sig);
+ if (chain_start_cells.count(c) != 0)
+ break;
+ }
+
+ return chain;
+ }
+
+ void process_chain(vector<Cell*> &chain)
+ {
+ if (GetSize(chain) < 2)
+ return;
+
+ int cursor = 0;
+ while (cursor < GetSize(chain))
+ {
+ int cases = GetSize(chain) - cursor;
+
+ Cell *first_cell = chain[cursor];
+ dict<int, SigBit> taps_dict;
+
+ if (cases < 2) {
+ cursor++;
+ continue;
+ }
+
+ Cell *last_cell = chain[cursor+cases-1];
+
+ log("Converting %s.%s ... %s.%s to a pmux with %d cases.\n",
+ log_id(module), log_id(first_cell), log_id(module), log_id(last_cell), cases);
+
+ mux_count += cases;
+ pmux_count += 1;
+
+ first_cell->type = "$pmux";
+ SigSpec b_sig = first_cell->getPort("\\B");
+ SigSpec s_sig = first_cell->getPort("\\S");
+
+ for (int i = 1; i < cases; i++) {
+ Cell* prev_cell = chain[cursor+i-1];
+ Cell* cursor_cell = chain[cursor+i];
+ if (sigmap(prev_cell->getPort("\\Y")) == sigmap(cursor_cell->getPort("\\A"))) {
+ b_sig.append(cursor_cell->getPort("\\B"));
+ s_sig.append(cursor_cell->getPort("\\S"));
+ }
+ else {
+ log_assert(cursor_cell->type == "$mux");
+ b_sig.append(cursor_cell->getPort("\\A"));
+ s_sig.append(module->LogicNot(NEW_ID, cursor_cell->getPort("\\S")));
+ }
+ remove_cells.insert(cursor_cell);
+ }
+
+ first_cell->setPort("\\B", b_sig);
+ first_cell->setPort("\\S", s_sig);
+ first_cell->setParam("\\S_WIDTH", GetSize(s_sig));
+ first_cell->setPort("\\Y", last_cell->getPort("\\Y"));
+
+ cursor += cases;
+ }
+ }
+
+ void cleanup()
+ {
+ for (auto cell : remove_cells)
+ module->remove(cell);
+
+ remove_cells.clear();
+ sig_chain_next.clear();
+ sig_chain_prev.clear();
+ chain_start_cells.clear();
+ candidate_cells.clear();
+ }
+
+ MuxpackWorker(Module *module) :
+ module(module), sigmap(module), mux_count(0), pmux_count(0), excl_db(module, sigmap)
+ {
+ make_sig_chain_next_prev();
+ find_chain_start_cells();
+
+ for (auto c : chain_start_cells) {
+ vector<Cell*> chain = create_chain(c);
+ process_chain(chain);
+ }
+
+ cleanup();
+ }
+};
+
+struct MuxpackPass : public Pass {
+ MuxpackPass() : Pass("muxpack", "$mux/$pmux cascades to $pmux") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" muxpack [selection]\n");
+ log("\n");
+ log("This pass converts cascaded chains of $pmux cells (e.g. those create from case\n");
+ log("constructs) and $mux cells (e.g. those created by if-else constructs) into\n");
+ log("$pmux cells.\n");
+ log("\n");
+ log("This optimisation is conservative --- it will only pack $mux or $pmux cells\n");
+ log("whose select lines are driven by '$eq' cells with other such cells if it can be\n");
+ log("certain that their select inputs are mutually exclusive.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing MUXPACK pass ($mux cell cascades to $pmux).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ int mux_count = 0;
+ int pmux_count = 0;
+
+ for (auto module : design->selected_modules()) {
+ MuxpackWorker worker(module);
+ mux_count += worker.mux_count;
+ pmux_count += worker.pmux_count;
+ }
+
+ log("Converted %d (p)mux cells into %d pmux cells.\n", mux_count, pmux_count);
+ }
+} MuxpackPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc
index c3b13aca..a8a8e0bc 100644
--- a/passes/opt/opt_clean.cc
+++ b/passes/opt/opt_clean.cc
@@ -64,7 +64,7 @@ struct keep_cache_t
bool query(Cell *cell)
{
- if (cell->type.in("$memwr", "$meminit", "$assert", "$assume", "$live", "$fair", "$cover"))
+ if (cell->type.in("$memwr", "$meminit", "$assert", "$assume", "$live", "$fair", "$cover", "$specify2", "$specify3", "$specrule"))
return true;
if (cell->has_keep_attr())
@@ -85,22 +85,34 @@ void rmunused_module_cells(Module *module, bool verbose)
{
SigMap sigmap(module);
pool<Cell*> queue, unused;
+ pool<SigBit> used_raw_bits;
dict<SigBit, pool<Cell*>> wire2driver;
+ dict<SigBit, vector<string>> driver_driver_logs;
+
+ SigMap raw_sigmap;
+ for (auto &it : module->connections_) {
+ for (int i = 0; i < GetSize(it.second); i++) {
+ if (it.second[i].wire != nullptr)
+ raw_sigmap.add(it.first[i], it.second[i]);
+ }
+ }
for (auto &it : module->cells_) {
Cell *cell = it.second;
for (auto &it2 : cell->connections()) {
- if (!ct_all.cell_known(cell->type) || ct_all.cell_output(cell->type, it2.first))
- for (auto raw_bit : it2.second) {
- if (raw_bit.wire == nullptr)
- continue;
- auto bit = sigmap(raw_bit);
- if (bit.wire == nullptr)
- log_warning("Driver-driver conflict for %s between cell %s.%s and constant %s in %s: Resolved using constant.\n",
- log_signal(raw_bit), log_id(cell), log_id(it2.first), log_signal(bit), log_id(module));
- if (bit.wire != nullptr)
- wire2driver[bit].insert(cell);
- }
+ if (ct_all.cell_known(cell->type) && !ct_all.cell_output(cell->type, it2.first))
+ continue;
+ for (auto raw_bit : it2.second) {
+ if (raw_bit.wire == nullptr)
+ continue;
+ auto bit = sigmap(raw_bit);
+ if (bit.wire == nullptr && ct_all.cell_known(cell->type))
+ driver_driver_logs[raw_sigmap(raw_bit)].push_back(stringf("Driver-driver conflict "
+ "for %s between cell %s.%s and constant %s in %s: Resolved using constant.",
+ log_signal(raw_bit), log_id(cell), log_id(it2.first), log_signal(bit), log_id(module)));
+ if (bit.wire != nullptr)
+ wire2driver[bit].insert(cell);
+ }
}
if (keep_cache.query(cell))
queue.insert(cell);
@@ -114,6 +126,8 @@ void rmunused_module_cells(Module *module, bool verbose)
for (auto bit : sigmap(wire))
for (auto c : wire2driver[bit])
queue.insert(c), unused.erase(c);
+ for (auto raw_bit : SigSpec(wire))
+ used_raw_bits.insert(raw_sigmap(raw_bit));
}
}
@@ -137,11 +151,27 @@ void rmunused_module_cells(Module *module, bool verbose)
for (auto cell : unused) {
if (verbose)
- log(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str());
+ log_debug(" removing unused `%s' cell `%s'.\n", cell->type.c_str(), cell->name.c_str());
module->design->scratchpad_set_bool("opt.did_something", true);
module->remove(cell);
count_rm_cells++;
}
+
+ for (auto &it : module->cells_) {
+ Cell *cell = it.second;
+ for (auto &it2 : cell->connections()) {
+ if (ct_all.cell_known(cell->type) && !ct_all.cell_input(cell->type, it2.first))
+ continue;
+ for (auto raw_bit : raw_sigmap(it2.second))
+ used_raw_bits.insert(raw_bit);
+ }
+ }
+
+ for (auto it : driver_driver_logs) {
+ if (used_raw_bits.count(it.first))
+ for (auto msg : it.second)
+ log_warning("%s\n", msg.c_str());
+ }
}
int count_nontrivial_wire_attrs(RTLIL::Wire *w)
@@ -202,7 +232,7 @@ bool check_public_name(RTLIL::IdString id)
return true;
}
-void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose)
+bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbose)
{
SigPool register_signals;
SigPool connected_signals;
@@ -245,11 +275,13 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
module->connections_.clear();
SigPool used_signals;
+ SigPool raw_used_signals;
SigPool used_signals_nodrivers;
for (auto &it : module->cells_) {
RTLIL::Cell *cell = it.second;
for (auto &it2 : cell->connections_) {
assign_map.apply(it2.second);
+ raw_used_signals.add(it2.second);
used_signals.add(it2.second);
if (!ct_all.cell_output(cell->type, it2.first))
used_signals_nodrivers.add(it2.second);
@@ -259,6 +291,7 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
RTLIL::Wire *wire = it.second;
if (wire->port_id > 0) {
RTLIL::SigSpec sig = RTLIL::SigSpec(wire);
+ raw_used_signals.add(sig);
assign_map.apply(sig);
used_signals.add(sig);
if (!wire->port_input)
@@ -271,72 +304,103 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos
}
}
- std::vector<RTLIL::Wire*> maybe_del_wires;
+ pool<RTLIL::Wire*> del_wires_queue;
for (auto wire : module->wires())
{
- if ((!purge_mode && check_public_name(wire->name)) || wire->port_id != 0 || wire->get_bool_attribute("\\keep") || wire->attributes.count("\\init")) {
- RTLIL::SigSpec s1 = RTLIL::SigSpec(wire), s2 = s1;
- assign_map.apply(s2);
- if (!used_signals.check_any(s2) && wire->port_id == 0 && !wire->get_bool_attribute("\\keep")) {
- maybe_del_wires.push_back(wire);
- } else {
- log_assert(GetSize(s1) == GetSize(s2));
- RTLIL::SigSig new_conn;
- for (int i = 0; i < GetSize(s1); i++)
- if (s1[i] != s2[i]) {
- new_conn.first.append_bit(s1[i]);
- new_conn.second.append_bit(s2[i]);
+ SigSpec s1 = SigSpec(wire), s2 = assign_map(s1);
+ log_assert(GetSize(s1) == GetSize(s2));
+
+ Const initval;
+ if (wire->attributes.count("\\init"))
+ initval = wire->attributes.at("\\init");
+ if (GetSize(initval) != GetSize(wire))
+ initval.bits.resize(GetSize(wire), State::Sx);
+ if (initval.is_fully_undef())
+ wire->attributes.erase("\\init");
+
+ if (GetSize(wire) == 0) {
+ // delete zero-width wires, unless they are module ports
+ if (wire->port_id == 0)
+ goto delete_this_wire;
+ } else
+ if (wire->port_id != 0 || wire->get_bool_attribute("\\keep") || !initval.is_fully_undef()) {
+ // do not delete anything with "keep" or module ports or initialized wires
+ } else
+ if (!purge_mode && check_public_name(wire->name) && (raw_used_signals.check_any(s1) || used_signals.check_any(s2) || s1 != s2)) {
+ // do not get rid of public names unless in purge mode or if the wire is entirely unused, not even aliased
+ } else
+ if (!raw_used_signals.check_any(s1)) {
+ // delete wires that aren't used by anything directly
+ goto delete_this_wire;
+ } else
+ if (!used_signals.check_any(s2)) {
+ // delete wires that aren't used by anything indirectly, even though other wires may alias it
+ goto delete_this_wire;
+ }
+
+ if (0)
+ {
+ delete_this_wire:
+ del_wires_queue.insert(wire);
+ }
+ else
+ {
+ RTLIL::SigSig new_conn;
+ for (int i = 0; i < GetSize(s1); i++)
+ if (s1[i] != s2[i]) {
+ if (s2[i] == State::Sx && (initval[i] == State::S0 || initval[i] == State::S1)) {
+ s2[i] = initval[i];
+ initval[i] = State::Sx;
}
- if (new_conn.first.size() > 0) {
- used_signals.add(new_conn.first);
- used_signals.add(new_conn.second);
- module->connect(new_conn);
+ new_conn.first.append_bit(s1[i]);
+ new_conn.second.append_bit(s2[i]);
}
+ if (new_conn.first.size() > 0) {
+ if (initval.is_fully_undef())
+ wire->attributes.erase("\\init");
+ else
+ wire->attributes.at("\\init") = initval;
+ used_signals.add(new_conn.first);
+ used_signals.add(new_conn.second);
+ module->connect(new_conn);
}
- } else {
- if (!used_signals.check_any(RTLIL::SigSpec(wire)))
- maybe_del_wires.push_back(wire);
- }
- RTLIL::SigSpec sig = assign_map(RTLIL::SigSpec(wire));
- if (!used_signals_nodrivers.check_any(sig)) {
- std::string unused_bits;
- for (int i = 0; i < GetSize(sig); i++) {
- if (sig[i].wire == NULL)
- continue;
- if (!used_signals_nodrivers.check(sig[i])) {
- if (!unused_bits.empty())
- unused_bits += " ";
- unused_bits += stringf("%d", i);
+ if (!used_signals_nodrivers.check_all(s2)) {
+ std::string unused_bits;
+ for (int i = 0; i < GetSize(s2); i++) {
+ if (s2[i].wire == NULL)
+ continue;
+ if (!used_signals_nodrivers.check(s2[i])) {
+ if (!unused_bits.empty())
+ unused_bits += " ";
+ unused_bits += stringf("%d", i);
+ }
}
- }
- if (unused_bits.empty() || wire->port_id != 0)
+ if (unused_bits.empty() || wire->port_id != 0)
+ wire->attributes.erase("\\unused_bits");
+ else
+ wire->attributes["\\unused_bits"] = RTLIL::Const(unused_bits);
+ } else {
wire->attributes.erase("\\unused_bits");
- else
- wire->attributes["\\unused_bits"] = RTLIL::Const(unused_bits);
- } else {
- wire->attributes.erase("\\unused_bits");
+ }
}
}
+ int del_temp_wires_count = 0;
+ for (auto wire : del_wires_queue) {
+ if (ys_debug() || (check_public_name(wire->name) && verbose))
+ log_debug(" removing unused non-port wire %s.\n", wire->name.c_str());
+ else
+ del_temp_wires_count++;
+ }
- pool<RTLIL::Wire*> del_wires;
+ module->remove(del_wires_queue);
+ count_rm_wires += GetSize(del_wires_queue);
- int del_wires_count = 0;
- for (auto wire : maybe_del_wires)
- if (!used_signals.check_any(RTLIL::SigSpec(wire))) {
- if (check_public_name(wire->name) && verbose) {
- log(" removing unused non-port wire %s.\n", wire->name.c_str());
- }
- del_wires.insert(wire);
- del_wires_count++;
- }
+ if (verbose && del_temp_wires_count)
+ log_debug(" removed %d unused temporary wires.\n", del_temp_wires_count);
- module->remove(del_wires);
- count_rm_wires += del_wires.size();
-
- if (verbose && del_wires_count > 0)
- log(" removed %d unused temporary wires.\n", del_wires_count);
+ return !del_wires_queue.empty();
}
bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose)
@@ -399,7 +463,7 @@ bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose)
}
if (verbose)
- log(" removing redundant init attribute on %s.\n", log_id(wire));
+ log_debug(" removing redundant init attribute on %s.\n", log_id(wire));
wire->attributes.erase("\\init");
did_something = true;
@@ -416,7 +480,7 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool
std::vector<RTLIL::Cell*> delcells;
for (auto cell : module->cells())
- if (cell->type.in("$pos", "$_BUF_")) {
+ if (cell->type.in("$pos", "$_BUF_") && !cell->has_keep_attr()) {
bool is_signed = cell->type == "$pos" && cell->getParam("\\A_SIGNED").as_bool();
RTLIL::SigSpec a = cell->getPort("\\A");
RTLIL::SigSpec y = cell->getPort("\\Y");
@@ -426,7 +490,7 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool
}
for (auto cell : delcells) {
if (verbose)
- log(" removing buffer cell `%s': %s = %s\n", cell->name.c_str(),
+ log_debug(" removing buffer cell `%s': %s = %s\n", cell->name.c_str(),
log_signal(cell->getPort("\\Y")), log_signal(cell->getPort("\\A")));
module->remove(cell);
}
@@ -434,10 +498,10 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool
module->design->scratchpad_set_bool("opt.did_something", true);
rmunused_module_cells(module, verbose);
- rmunused_module_signals(module, purge_mode, verbose);
+ while (rmunused_module_signals(module, purge_mode, verbose)) { }
if (rminit && rmunused_module_init(module, purge_mode, verbose))
- rmunused_module_signals(module, purge_mode, verbose);
+ while (rmunused_module_signals(module, purge_mode, verbose)) { }
}
struct OptCleanPass : public Pass {
@@ -483,6 +547,9 @@ struct OptCleanPass : public Pass {
ct_all.setup(design);
+ count_rm_cells = 0;
+ count_rm_wires = 0;
+
for (auto module : design->selected_whole_modules_warn()) {
if (module->has_processes_warn())
continue;
@@ -548,9 +615,10 @@ struct CleanPass : public Pass {
for (auto module : design->selected_whole_modules()) {
if (module->has_processes())
continue;
- rmunused_module(module, purge_mode, false, false);
+ rmunused_module(module, purge_mode, ys_debug(), false);
}
+ log_suppressed();
if (count_rm_cells > 0 || count_rm_wires > 0)
log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires);
diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc
index a05db2a4..512ef0cb 100644
--- a/passes/opt/opt_expr.cc
+++ b/passes/opt/opt_expr.cc
@@ -39,6 +39,9 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
SigPool used_signals;
SigPool all_signals;
+ dict<SigBit, pair<Wire*, State>> initbits;
+ pool<Wire*> revisit_initwires;
+
for (auto cell : module->cells())
for (auto &conn : cell->connections()) {
if (!ct.cell_known(cell->type) || ct.cell_output(cell->type, conn.first))
@@ -48,9 +51,17 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
}
for (auto wire : module->wires()) {
+ if (wire->attributes.count("\\init")) {
+ SigSpec sig = sigmap(wire);
+ Const initval = wire->attributes.at("\\init");
+ for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++) {
+ if (initval[i] == State::S0 || initval[i] == State::S1)
+ initbits[sig[i]] = make_pair(wire, initval[i]);
+ }
+ }
if (wire->port_input)
driven_signals.add(sigmap(wire));
- if (wire->port_output)
+ if (wire->port_output || wire->get_bool_attribute("\\keep"))
used_signals.add(sigmap(wire));
all_signals.add(sigmap(wire));
}
@@ -67,10 +78,43 @@ void replace_undriven(RTLIL::Design *design, RTLIL::Module *module)
if (sig.size() == 0)
continue;
- log("Setting undriven signal in %s to undef: %s\n", RTLIL::id2cstr(module->name), log_signal(c));
- module->connect(RTLIL::SigSig(c, RTLIL::SigSpec(RTLIL::State::Sx, c.width)));
+ Const val(RTLIL::State::Sx, GetSize(sig));
+ for (int i = 0; i < GetSize(sig); i++) {
+ SigBit bit = sigmap(sig[i]);
+ auto cursor = initbits.find(bit);
+ if (cursor != initbits.end()) {
+ revisit_initwires.insert(cursor->second.first);
+ val[i] = cursor->second.second;
+ }
+ }
+
+ log_debug("Setting undriven signal in %s to constant: %s = %s\n", log_id(module), log_signal(sig), log_signal(val));
+ module->connect(sig, val);
did_something = true;
}
+
+ if (!revisit_initwires.empty())
+ {
+ SigMap sm2(module);
+
+ for (auto wire : revisit_initwires) {
+ SigSpec sig = sm2(wire);
+ Const initval = wire->attributes.at("\\init");
+ for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++) {
+ if (SigBit(initval[i]) == sig[i])
+ initval[i] = State::Sx;
+ }
+ if (initval.is_fully_undef()) {
+ log_debug("Removing init attribute from %s/%s.\n", log_id(module), log_id(wire));
+ wire->attributes.erase("\\init");
+ did_something = true;
+ } else if (initval != wire->attributes.at("\\init")) {
+ log_debug("Updating init attribute on %s/%s: %s\n", log_id(module), log_id(wire), log_signal(initval));
+ wire->attributes["\\init"] = initval;
+ did_something = true;
+ }
+ }
+ }
}
void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell, std::string info, std::string out_port, RTLIL::SigSpec out_val)
@@ -78,7 +122,7 @@ void replace_cell(SigMap &assign_map, RTLIL::Module *module, RTLIL::Cell *cell,
RTLIL::SigSpec Y = cell->getPort(out_port);
out_val.extend_u0(Y.size(), false);
- log("Replacing %s cell `%s' (%s) in module `%s' with constant driver `%s = %s'.\n",
+ log_debug("Replacing %s cell `%s' (%s) in module `%s' with constant driver `%s = %s'.\n",
cell->type.c_str(), cell->name.c_str(), info.c_str(),
module->name.c_str(), log_signal(Y), log_signal(out_val));
// log_cell(cell);
@@ -134,7 +178,7 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
if (GetSize(grouped_bits[i]) == GetSize(bits_y))
return false;
- log("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n",
+ log_debug("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n",
log_id(cell->type), log_id(cell), log_id(module));
for (int i = 0; i < GRP_N; i++)
@@ -156,7 +200,7 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
}
if (cell->type.in("$and", "$or") && i == GRP_CONST_A) {
- log(" Direct Connection: %s (%s with %s)\n", log_signal(new_b), log_id(cell->type), log_signal(new_a));
+ log_debug(" Direct Connection: %s (%s with %s)\n", log_signal(new_b), log_id(cell->type), log_signal(new_a));
module->connect(new_y, new_b);
module->connect(new_conn);
continue;
@@ -180,10 +224,10 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
module->connect(new_conn);
- log(" New cell `%s': A=%s", log_id(c), log_signal(new_a));
+ log_debug(" New cell `%s': A=%s", log_id(c), log_signal(new_a));
if (b_name == "\\B")
- log(", B=%s", log_signal(new_b));
- log("\n");
+ log_debug(", B=%s", log_signal(new_b));
+ log_debug("\n");
}
cover_list("opt.opt_expr.fine.group", "$not", "$pos", "$and", "$or", "$xor", "$xnor", cell->type.str());
@@ -197,7 +241,7 @@ void handle_polarity_inv(Cell *cell, IdString port, IdString param, const SigMap
{
SigSpec sig = assign_map(cell->getPort(port));
if (invert_map.count(sig)) {
- log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
+ log_debug("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module),
log_signal(sig), log_signal(invert_map.at(sig)));
cell->setPort(port, (invert_map.at(sig)));
@@ -226,7 +270,7 @@ void handle_clkpol_celltype_swap(Cell *cell, string type1, string type2, IdStrin
if (cell->type.in(type1, type2)) {
SigSpec sig = assign_map(cell->getPort(port));
if (invert_map.count(sig)) {
- log("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
+ log_debug("Inverting %s of %s cell `%s' in module `%s': %s -> %s\n",
log_id(port), log_id(cell->type), log_id(cell), log_id(cell->module),
log_signal(sig), log_signal(invert_map.at(sig)));
cell->setPort(port, (invert_map.at(sig)));
@@ -455,9 +499,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
{
if (cell->type == "$reduce_xnor") {
cover("opt.opt_expr.reduce_xnor_not");
- log("Replacing %s cell `%s' in module `%s' with $not cell.\n",
+ log_debug("Replacing %s cell `%s' in module `%s' with $not cell.\n",
log_id(cell->type), log_id(cell->name), log_id(module));
cell->type = "$not";
+ did_something = true;
} else {
cover("opt.opt_expr.unary_buffer");
replace_cell(assign_map, module, cell, "unary_buffer", "\\Y", cell->getPort("\\A"));
@@ -488,7 +533,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (GetSize(new_sig_a) < GetSize(sig_a)) {
cover_list("opt.opt_expr.fine.neutral_A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_and", "$reduce_bool", cell->type.str());
- log("Replacing port A of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
+ log_debug("Replacing port A of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_sig_a));
cell->setPort("\\A", new_sig_a);
cell->parameters.at("\\A_WIDTH") = GetSize(new_sig_a);
@@ -511,7 +556,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (GetSize(new_sig_b) < GetSize(sig_b)) {
cover_list("opt.opt_expr.fine.neutral_B", "$logic_and", "$logic_or", cell->type.str());
- log("Replacing port B of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
+ log_debug("Replacing port B of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_sig_b));
cell->setPort("\\B", new_sig_b);
cell->parameters.at("\\B_WIDTH") = GetSize(new_sig_b);
@@ -537,7 +582,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
cover("opt.opt_expr.fine.$reduce_and");
- log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
+ log_debug("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a));
cell->setPort("\\A", sig_a = new_a);
cell->parameters.at("\\A_WIDTH") = 1;
@@ -563,7 +608,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (new_a != RTLIL::State::Sm && RTLIL::SigSpec(new_a) != sig_a) {
cover_list("opt.opt_expr.fine.A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_bool", cell->type.str());
- log("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
+ log_debug("Replacing port A of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_a));
cell->setPort("\\A", sig_a = new_a);
cell->parameters.at("\\A_WIDTH") = 1;
@@ -589,7 +634,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (new_b != RTLIL::State::Sm && RTLIL::SigSpec(new_b) != sig_b) {
cover_list("opt.opt_expr.fine.B", "$logic_and", "$logic_or", cell->type.str());
- log("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
+ log_debug("Replacing port B of %s cell `%s' in module `%s' with constant driver: %s -> %s\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_b));
cell->setPort("\\B", sig_b = new_b);
cell->parameters.at("\\B_WIDTH") = 1;
@@ -640,7 +685,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if ((cell->type == "$_MUX_" || cell->type == "$mux") && invert_map.count(assign_map(cell->getPort("\\S"))) != 0) {
cover_list("opt.opt_expr.invert.muxsel", "$_MUX_", "$mux", cell->type.str());
- log("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module));
+ log_debug("Optimizing away select inverter for %s cell `%s' in module `%s'.\n", log_id(cell->type), log_id(cell), log_id(module));
RTLIL::SigSpec tmp = cell->getPort("\\A");
cell->setPort("\\A", cell->getPort("\\B"));
cell->setPort("\\B", tmp);
@@ -750,7 +795,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
ACTION_DO("\\Y", cell->getPort("\\A"));
if (input == State::S0 && !a.is_fully_undef()) {
cover("opt.opt_expr.action_" S__LINE__);
- log("Replacing data input of %s cell `%s' in module `%s' with constant undef.\n",
+ log_debug("Replacing data input of %s cell `%s' in module `%s' with constant undef.\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str());
cell->setPort("\\A", SigSpec(State::Sx, GetSize(a)));
did_something = true;
@@ -822,7 +867,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
ACTION_DO("\\Y", cell->getPort("\\A"));
} else {
cover_list("opt.opt_expr.eqneq.isnot", "$eq", "$ne", cell->type.str());
- log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
+ log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
cell->type = "$not";
cell->parameters.erase("\\B_WIDTH");
cell->parameters.erase("\\B_SIGNED");
@@ -837,7 +882,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
(assign_map(cell->getPort("\\A")).is_fully_zero() || assign_map(cell->getPort("\\B")).is_fully_zero()))
{
cover_list("opt.opt_expr.eqneq.cmpzero", "$eq", "$ne", cell->type.str());
- log("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
+ log_debug("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell),
log_id(module), "$eq" ? "$logic_not" : "$reduce_bool");
cell->type = cell->type == "$eq" ? "$logic_not" : "$reduce_bool";
if (assign_map(cell->getPort("\\A")).is_fully_zero()) {
@@ -876,7 +921,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
cover_list("opt.opt_expr.constshift", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", cell->type.str());
- log("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n",
+ log_debug("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n",
log_id(cell->type), log_id(cell), log_signal(assign_map(cell->getPort("\\B"))), shift_bits, log_id(module), log_signal(sig_y));
module->connect(cell->getPort("\\Y"), sig_y);
@@ -939,7 +984,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (identity_wrt_b)
cover_list("opt.opt_expr.identwrt.b", "$add", "$sub", "$or", "$xor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$mul", "$div", cell->type.str());
- log("Replacing %s cell `%s' in module `%s' with identity for port %c.\n",
+ log_debug("Replacing %s cell `%s' in module `%s' with identity for port %c.\n",
cell->type.c_str(), cell->name.c_str(), module->name.c_str(), identity_wrt_a ? 'A' : 'B');
if (!identity_wrt_a) {
@@ -969,7 +1014,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") &&
cell->getPort("\\A") == RTLIL::SigSpec(1, 1) && cell->getPort("\\B") == RTLIL::SigSpec(0, 1)) {
cover_list("opt.opt_expr.mux_invert", "$mux", "$_MUX_", cell->type.str());
- log("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
+ log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module));
cell->setPort("\\A", cell->getPort("\\S"));
cell->unsetPort("\\B");
cell->unsetPort("\\S");
@@ -988,7 +1033,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\A") == RTLIL::SigSpec(0, 1)) {
cover_list("opt.opt_expr.mux_and", "$mux", "$_MUX_", cell->type.str());
- log("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
+ log_debug("Replacing %s cell `%s' in module `%s' with and-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
cell->setPort("\\A", cell->getPort("\\S"));
cell->unsetPort("\\S");
if (cell->type == "$mux") {
@@ -1008,7 +1053,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (consume_x && mux_bool && (cell->type == "$mux" || cell->type == "$_MUX_") && cell->getPort("\\B") == RTLIL::SigSpec(1, 1)) {
cover_list("opt.opt_expr.mux_or", "$mux", "$_MUX_", cell->type.str());
- log("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
+ log_debug("Replacing %s cell `%s' in module `%s' with or-gate.\n", log_id(cell->type), log_id(cell), log_id(module));
cell->setPort("\\B", cell->getPort("\\S"));
cell->unsetPort("\\S");
if (cell->type == "$mux") {
@@ -1061,7 +1106,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (cell->getPort("\\S").size() != new_s.size()) {
cover_list("opt.opt_expr.mux_reduce", "$mux", "$pmux", cell->type.str());
- log("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n",
+ log_debug("Optimized away %d select inputs of %s cell `%s' in module `%s'.\n",
GetSize(cell->getPort("\\S")) - GetSize(new_s), log_id(cell->type), log_id(cell), log_id(module));
cell->setPort("\\A", new_a);
cell->setPort("\\B", new_b);
@@ -1179,7 +1224,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
{
cover("opt.opt_expr.mul_shift.zero");
- log("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n",
+ log_debug("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n",
cell->name.c_str(), module->name.c_str());
module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(0, sig_y.size())));
@@ -1197,7 +1242,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
else
cover("opt.opt_expr.mul_shift.unswapped");
- log("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
+ log_debug("Replacing multiply-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
a_val, cell->name.c_str(), module->name.c_str(), i);
if (!swapped_ab) {
@@ -1237,7 +1282,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
{
cover("opt.opt_expr.divmod_zero");
- log("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n",
+ log_debug("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n",
cell->name.c_str(), module->name.c_str());
module->connect(RTLIL::SigSig(sig_y, RTLIL::SigSpec(State::Sx, sig_y.size())));
@@ -1254,7 +1299,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
{
cover("opt.opt_expr.div_shift");
- log("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
+ log_debug("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
b_val, cell->name.c_str(), module->name.c_str(), i);
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
@@ -1272,7 +1317,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
{
cover("opt.opt_expr.mod_mask");
- log("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
+ log_debug("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
b_val, cell->name.c_str(), module->name.c_str());
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i);
@@ -1342,7 +1387,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
SigSpec y_sig = cell->getPort("\\Y");
Const y_value(cell->type.in("$eq", "$eqx") ? 0 : 1, GetSize(y_sig));
- log("Replacing cell `%s' in module `%s' with constant driver %s.\n",
+ log_debug("Replacing cell `%s' in module `%s' with constant driver %s.\n",
log_id(cell), log_id(module), log_signal(y_value));
module->connect(y_sig, y_value);
@@ -1354,7 +1399,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (redundant_bits)
{
- log("Removed %d redundant input bits from %s cell `%s' in module `%s'.\n",
+ log_debug("Removed %d redundant input bits from %s cell `%s' in module `%s'.\n",
redundant_bits, log_id(cell->type), log_id(cell), log_id(module));
cell->setPort("\\A", sig_a);
@@ -1493,7 +1538,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (replace || remove)
{
- log("Replacing %s cell `%s' (implementing %s) with %s.\n",
+ log_debug("Replacing %s cell `%s' (implementing %s) with %s.\n",
log_id(cell->type), log_id(cell), condition.c_str(), replacement.c_str());
if (replace)
module->connect(cell->getPort("\\Y"), replace_sig);
@@ -1599,8 +1644,14 @@ struct OptExprPass : public Pass {
for (auto module : design->selected_modules())
{
- if (undriven)
+ log("Optimizing module %s.\n", log_id(module));
+
+ if (undriven) {
+ did_something = false;
replace_undriven(design, module);
+ if (did_something)
+ design->scratchpad_set_bool("opt.did_something", true);
+ }
do {
do {
@@ -1610,7 +1661,11 @@ struct OptExprPass : public Pass {
design->scratchpad_set_bool("opt.did_something", true);
} while (did_something);
replace_const_cells(design, module, true, mux_undef, mux_bool, do_fine, keepdc, clkinv);
+ if (did_something)
+ design->scratchpad_set_bool("opt.did_something", true);
} while (did_something);
+
+ log_suppressed();
}
log_pop();
diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc
index 26855fd7..dda10ec1 100644
--- a/passes/opt/opt_lut.cc
+++ b/passes/opt/opt_lut.cc
@@ -81,7 +81,7 @@ struct OptLutWorker
}
}
- log("Number of LUTs: %8zu\n", luts.size());
+ log("Number of LUTs: %8d\n", GetSize(luts));
for (int arity = 1; arity <= max_arity; arity++)
{
if (arity_counts[arity])
@@ -353,14 +353,14 @@ struct OptLutWorker
int lutM_arity = lutA_arity + lutB_arity - 1 - common_inputs.size();
if (lutA_dlogic_inputs.size())
- log(" Cell A is a %d-LUT with %zu dedicated connections. ", lutA_arity, lutA_dlogic_inputs.size());
+ log(" Cell A is a %d-LUT with %d dedicated connections. ", lutA_arity, GetSize(lutA_dlogic_inputs));
else
log(" Cell A is a %d-LUT. ", lutA_arity);
if (lutB_dlogic_inputs.size())
- log("Cell B is a %d-LUT with %zu dedicated connections.\n", lutB_arity, lutB_dlogic_inputs.size());
+ log("Cell B is a %d-LUT with %d dedicated connections.\n", lutB_arity, GetSize(lutB_dlogic_inputs));
else
log("Cell B is a %d-LUT.\n", lutB_arity);
- log(" Cells share %zu input(s) and can be merged into one %d-LUT.\n", common_inputs.size(), lutM_arity);
+ log(" Cells share %d input(s) and can be merged into one %d-LUT.\n", GetSize(common_inputs), lutM_arity);
const int COMBINE_A = 1, COMBINE_B = 2, COMBINE_EITHER = COMBINE_A | COMBINE_B;
int combine_mask = 0;
diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc
index eedf8890..7567d465 100644
--- a/passes/opt/opt_merge.cc
+++ b/passes/opt/opt_merge.cc
@@ -315,17 +315,17 @@ struct OptMergeWorker
{
if (sharemap.count(cell) > 0) {
did_something = true;
- log(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str());
+ log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), sharemap[cell]->name.c_str());
for (auto &it : cell->connections()) {
if (cell->output(it.first)) {
RTLIL::SigSpec other_sig = sharemap[cell]->getPort(it.first);
- log(" Redirecting output %s: %s = %s\n", it.first.c_str(),
+ log_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(),
log_signal(it.second), log_signal(other_sig));
module->connect(RTLIL::SigSig(it.second, other_sig));
assign_map.add(it.second, other_sig);
}
}
- log(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str());
+ log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str());
#ifdef USE_CELL_HASH_CACHE
cell_hash_cache.erase(cell);
#endif
@@ -336,6 +336,8 @@ struct OptMergeWorker
}
}
}
+
+ log_suppressed();
}
};
diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc
index 375697dc..6511e091 100644
--- a/passes/opt/opt_muxtree.cc
+++ b/passes/opt/opt_muxtree.cc
@@ -181,20 +181,29 @@ struct OptMuxtreeWorker
for (int mux_idx = 0; mux_idx < GetSize(root_muxes); mux_idx++)
if (root_muxes.at(mux_idx)) {
- log(" Root of a mux tree: %s%s\n", log_id(mux2info[mux_idx].cell), root_enable_muxes.at(mux_idx) ? " (pure)" : "");
+ log_debug(" Root of a mux tree: %s%s\n", log_id(mux2info[mux_idx].cell), root_enable_muxes.at(mux_idx) ? " (pure)" : "");
root_mux_rerun.erase(mux_idx);
eval_root_mux(mux_idx);
+ if (glob_abort_cnt == 0) {
+ log(" Giving up (too many iterations)\n");
+ return;
+ }
}
while (!root_mux_rerun.empty()) {
int mux_idx = *root_mux_rerun.begin();
- log(" Root of a mux tree: %s (rerun as non-pure)\n", log_id(mux2info[mux_idx].cell));
+ log_debug(" Root of a mux tree: %s (rerun as non-pure)\n", log_id(mux2info[mux_idx].cell));
log_assert(root_enable_muxes.at(mux_idx));
root_mux_rerun.erase(mux_idx);
eval_root_mux(mux_idx);
+ if (glob_abort_cnt == 0) {
+ log(" Giving up (too many iterations)\n");
+ return;
+ }
}
log(" Analyzing evaluation results.\n");
+ log_assert(glob_abort_cnt > 0);
for (auto &mi : mux2info)
{
@@ -326,7 +335,7 @@ struct OptMuxtreeWorker
if (abort_count == 0) {
root_mux_rerun.insert(m);
root_enable_muxes.at(m) = true;
- log(" Removing pure flag from root mux %s.\n", log_id(mux2info[m].cell));
+ log_debug(" Removing pure flag from root mux %s.\n", log_id(mux2info[m].cell));
} else
eval_mux(knowledge, m, false, do_enable_ports, abort_count - 1);
} else
@@ -397,10 +406,8 @@ struct OptMuxtreeWorker
void eval_mux(knowledge_t &knowledge, int mux_idx, bool do_replace_known, bool do_enable_ports, int abort_count)
{
- if (glob_abort_cnt == 0) {
- log(" Giving up (too many iterations)\n");
+ if (glob_abort_cnt == 0)
return;
- }
glob_abort_cnt--;
muxinfo_t &muxinfo = mux2info[mux_idx];
@@ -454,6 +461,7 @@ struct OptMuxtreeWorker
void eval_root_mux(int mux_idx)
{
+ log_assert(glob_abort_cnt > 0);
knowledge_t knowledge;
knowledge.known_inactive.resize(GetSize(bit2info));
knowledge.known_active.resize(GetSize(bit2info));
diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc
index e8570f0e..eeb992a3 100644
--- a/passes/opt/opt_rmdff.cc
+++ b/passes/opt/opt_rmdff.cc
@@ -260,8 +260,8 @@ delete_dlatch:
bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
{
- RTLIL::SigSpec sig_d, sig_q, sig_c, sig_r;
- RTLIL::Const val_cp, val_rp, val_rv;
+ RTLIL::SigSpec sig_d, sig_q, sig_c, sig_r, sig_e;
+ RTLIL::Const val_cp, val_rp, val_rv, val_ep;
if (dff->type == "$_FF_") {
sig_d = dff->getPort("\\D");
@@ -285,6 +285,16 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
val_rp = RTLIL::Const(dff->type[7] == 'P', 1);
val_rv = RTLIL::Const(dff->type[8] == '1', 1);
}
+ else if (dff->type.substr(0,7) == "$_DFFE_" && dff->type.substr(9) == "_" &&
+ (dff->type[7] == 'N' || dff->type[7] == 'P') &&
+ (dff->type[8] == 'N' || dff->type[8] == 'P')) {
+ sig_d = dff->getPort("\\D");
+ sig_q = dff->getPort("\\Q");
+ sig_c = dff->getPort("\\C");
+ sig_e = dff->getPort("\\E");
+ val_cp = RTLIL::Const(dff->type[7] == 'P', 1);
+ val_ep = RTLIL::Const(dff->type[8] == 'P', 1);
+ }
else if (dff->type == "$ff") {
sig_d = dff->getPort("\\D");
sig_q = dff->getPort("\\Q");
@@ -295,6 +305,14 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
sig_c = dff->getPort("\\CLK");
val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1);
}
+ else if (dff->type == "$dffe") {
+ sig_e = dff->getPort("\\EN");
+ sig_d = dff->getPort("\\D");
+ sig_q = dff->getPort("\\Q");
+ sig_c = dff->getPort("\\CLK");
+ val_cp = RTLIL::Const(dff->parameters["\\CLK_POLARITY"].as_bool(), 1);
+ val_ep = RTLIL::Const(dff->parameters["\\EN_POLARITY"].as_bool(), 1);
+ }
else if (dff->type == "$adff") {
sig_d = dff->getPort("\\D");
sig_q = dff->getPort("\\Q");
@@ -337,39 +355,60 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
}
}
+ // If clock is driven by a constant and (i) no reset signal
+ // (ii) Q has no initial value
+ // (iii) initial value is same as reset value
if (!sig_c.empty() && sig_c.is_fully_const() && (!sig_r.size() || !has_init || val_init == val_rv)) {
if (val_rv.bits.size() == 0)
val_rv = val_init;
+ // Q is permanently reset value or initial value
mod->connect(sig_q, val_rv);
goto delete_dff;
}
+ // If D is fully undefined and reset signal present and (i) Q has no initial value
+ // (ii) initial value is same as reset value
if (sig_d.is_fully_undef() && sig_r.size() && (!has_init || val_init == val_rv)) {
+ // Q is permanently reset value
mod->connect(sig_q, val_rv);
goto delete_dff;
}
+ // If D is fully undefined and no reset signal and Q has an initial value
if (sig_d.is_fully_undef() && !sig_r.size() && has_init) {
+ // Q is permanently initial value
mod->connect(sig_q, val_init);
goto delete_dff;
}
+ // If D is fully constant and (i) no reset signal
+ // (ii) reset value is same as constant D
+ // and (a) has no initial value
+ // (b) initial value same as constant D
if (sig_d.is_fully_const() && (!sig_r.size() || val_rv == sig_d.as_const()) && (!has_init || val_init == sig_d.as_const())) {
+ // Q is permanently D
mod->connect(sig_q, sig_d);
goto delete_dff;
}
+ // If D input is same as Q output and (i) no reset signal
+ // (ii) no initial signal
+ // (iii) initial value is same as reset value
if (sig_d == sig_q && (sig_r.empty() || !has_init || val_init == val_rv)) {
+ // Q is permanently reset value or initial value
if (sig_r.size())
mod->connect(sig_q, val_rv);
- if (has_init)
+ else if (has_init)
mod->connect(sig_q, val_init);
goto delete_dff;
}
+ // If reset signal is present, and is fully constant
if (!sig_r.empty() && sig_r.is_fully_const())
{
+ // If reset value is permanently active or if reset is undefined
if (sig_r == val_rp || sig_r.is_fully_undef()) {
+ // Q is permanently reset value
mod->connect(sig_q, val_rv);
goto delete_dff;
}
@@ -389,6 +428,30 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff)
dff->unsetPort("\\R");
}
+ // If enable signal is present, and is fully constant
+ if (!sig_e.empty() && sig_e.is_fully_const())
+ {
+ // If enable value is permanently inactive
+ if (sig_e != val_ep) {
+ // Q is permanently initial value
+ mod->connect(sig_q, val_init);
+ goto delete_dff;
+ }
+
+ log("Removing unused enable from %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod));
+
+ if (dff->type == "$dffe") {
+ dff->type = "$dff";
+ dff->unsetPort("\\EN");
+ dff->unsetParam("\\EN_POLARITY");
+ return true;
+ }
+
+ log_assert(dff->type.substr(0,7) == "$_DFFE_");
+ dff->type = stringf("$_DFF_%c_", + dff->type[7]);
+ dff->unsetPort("\\E");
+ }
+
return false;
delete_dff:
@@ -489,7 +552,8 @@ struct OptRmdffPass : public Pass {
if (cell->type.in("$_FF_", "$_DFF_N_", "$_DFF_P_",
"$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_",
"$_DFF_PN0_", "$_DFF_PN1_", "$_DFF_PP0_", "$_DFF_PP1_",
- "$ff", "$dff", "$adff"))
+ "$_DFFE_NN_", "$_DFFE_NP_", "$_DFFE_PN_", "$_DFFE_PP_",
+ "$ff", "$dff", "$dffe", "$adff"))
dff_list.push_back(cell->name);
if (cell->type.in("$dlatch", "$_DLATCH_P_", "$_DLATCH_N_"))
diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc
new file mode 100644
index 00000000..65d8b8f3
--- /dev/null
+++ b/passes/opt/pmux2shiftx.cc
@@ -0,0 +1,860 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct OnehotDatabase
+{
+ Module *module;
+ const SigMap &sigmap;
+ bool verbose = false;
+ bool initialized = false;
+
+ pool<SigBit> init_ones;
+ dict<SigSpec, pool<SigSpec>> sig_sources_db;
+ dict<SigSpec, bool> sig_onehot_cache;
+ pool<SigSpec> recursion_guard;
+
+ OnehotDatabase(Module *module, const SigMap &sigmap) : module(module), sigmap(sigmap)
+ {
+ }
+
+ void initialize()
+ {
+ log_assert(!initialized);
+ initialized = true;
+
+ for (auto wire : module->wires())
+ {
+ auto it = wire->attributes.find("\\init");
+ if (it == wire->attributes.end())
+ continue;
+
+ auto &val = it->second;
+ int width = std::max(GetSize(wire), GetSize(val));
+
+ for (int i = 0; i < width; i++)
+ if (val[i] == State::S1)
+ init_ones.insert(sigmap(SigBit(wire, i)));
+ }
+
+ for (auto cell : module->cells())
+ {
+ vector<SigSpec> inputs;
+ SigSpec output;
+
+ if (cell->type.in("$adff", "$dff", "$dffe", "$dlatch", "$ff"))
+ {
+ output = cell->getPort("\\Q");
+ if (cell->type == "$adff")
+ inputs.push_back(cell->getParam("\\ARST_VALUE"));
+ inputs.push_back(cell->getPort("\\D"));
+ }
+
+ if (cell->type.in("$mux", "$pmux"))
+ {
+ output = cell->getPort("\\Y");
+ inputs.push_back(cell->getPort("\\A"));
+ SigSpec B = cell->getPort("\\B");
+ for (int i = 0; i < GetSize(B); i += GetSize(output))
+ inputs.push_back(B.extract(i, GetSize(output)));
+ }
+
+ if (!output.empty())
+ {
+ output = sigmap(output);
+ auto &srcs = sig_sources_db[output];
+ for (auto src : inputs) {
+ while (!src.empty() && src[GetSize(src)-1] == State::S0)
+ src.remove(GetSize(src)-1);
+ srcs.insert(sigmap(src));
+ }
+ }
+ }
+ }
+
+ void query_worker(const SigSpec &sig, bool &retval, bool &cache, int indent)
+ {
+ if (verbose)
+ log("%*s %s\n", indent, "", log_signal(sig));
+ log_assert(retval);
+
+ if (recursion_guard.count(sig)) {
+ if (verbose)
+ log("%*s - recursion\n", indent, "");
+ cache = false;
+ return;
+ }
+
+ auto it = sig_onehot_cache.find(sig);
+ if (it != sig_onehot_cache.end()) {
+ if (verbose)
+ log("%*s - cached (%s)\n", indent, "", it->second ? "true" : "false");
+ if (!it->second)
+ retval = false;
+ return;
+ }
+
+ bool found_init_ones = false;
+ for (auto bit : sig) {
+ if (init_ones.count(bit)) {
+ if (found_init_ones) {
+ if (verbose)
+ log("%*s - non-onehot init value\n", indent, "");
+ retval = false;
+ break;
+ }
+ found_init_ones = true;
+ }
+ }
+
+ if (retval)
+ {
+ if (sig.is_fully_const())
+ {
+ bool found_ones = false;
+ for (auto bit : sig) {
+ if (bit == State::S1) {
+ if (found_ones) {
+ if (verbose)
+ log("%*s - non-onehot constant\n", indent, "");
+ retval = false;
+ break;
+ }
+ found_ones = true;
+ }
+ }
+ }
+ else
+ {
+ auto srcs = sig_sources_db.find(sig);
+ if (srcs == sig_sources_db.end()) {
+ if (verbose)
+ log("%*s - no sources for non-const signal\n", indent, "");
+ retval = false;
+ } else {
+ for (auto &src : srcs->second) {
+ bool child_cache = true;
+ recursion_guard.insert(sig);
+ query_worker(src, retval, child_cache, indent+4);
+ recursion_guard.erase(sig);
+ if (!child_cache)
+ cache = false;
+ if (!retval)
+ break;
+ }
+ }
+ }
+ }
+
+ // it is always safe to cache a negative result
+ if (cache || !retval)
+ sig_onehot_cache[sig] = retval;
+ }
+
+ bool query(const SigSpec &sig)
+ {
+ bool retval = true;
+ bool cache = true;
+
+ if (verbose)
+ log("** ONEHOT QUERY START (%s)\n", log_signal(sig));
+
+ if (!initialized)
+ initialize();
+
+ query_worker(sig, retval, cache, 3);
+
+ if (verbose)
+ log("** ONEHOT QUERY RESULT = %s\n", retval ? "true" : "false");
+
+ // it is always safe to cache the root result of a query
+ if (!cache)
+ sig_onehot_cache[sig] = retval;
+
+ return retval;
+ }
+};
+
+struct Pmux2ShiftxPass : public Pass {
+ Pmux2ShiftxPass() : Pass("pmux2shiftx", "transform $pmux cells to $shiftx cells") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" pmux2shiftx [options] [selection]\n");
+ log("\n");
+ log("This pass transforms $pmux cells to $shiftx cells.\n");
+ log("\n");
+ log(" -v, -vv\n");
+ log(" verbose output\n");
+ log("\n");
+ log(" -min_density <percentage>\n");
+ log(" specifies the minimum density for the shifter\n");
+ log(" default: 50\n");
+ log("\n");
+ log(" -min_choices <int>\n");
+ log(" specified the minimum number of choices for a control signal\n");
+ log(" default: 3\n");
+ log("\n");
+ log(" -onehot ignore|pmux|shiftx\n");
+ log(" select strategy for one-hot encoded control signals\n");
+ log(" default: pmux\n");
+ log("\n");
+ log(" -norange\n");
+ log(" disable $sub inference for \"range decoders\"\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ int min_density = 50;
+ int min_choices = 3;
+ bool allow_onehot = false;
+ bool optimize_onehot = true;
+ bool verbose = false;
+ bool verbose_onehot = false;
+ bool norange = false;
+
+ log_header(design, "Executing PMUX2SHIFTX pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-min_density" && argidx+1 < args.size()) {
+ min_density = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-min_choices" && argidx+1 < args.size()) {
+ min_choices = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "ignore") {
+ argidx++;
+ allow_onehot = false;
+ optimize_onehot = false;
+ continue;
+ }
+ if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "pmux") {
+ argidx++;
+ allow_onehot = false;
+ optimize_onehot = true;
+ continue;
+ }
+ if (args[argidx] == "-onehot" && argidx+1 < args.size() && args[argidx+1] == "shiftx") {
+ argidx++;
+ allow_onehot = true;
+ optimize_onehot = false;
+ continue;
+ }
+ if (args[argidx] == "-v") {
+ verbose = true;
+ continue;
+ }
+ if (args[argidx] == "-vv") {
+ verbose = true;
+ verbose_onehot = true;
+ continue;
+ }
+ if (args[argidx] == "-norange") {
+ norange = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ OnehotDatabase onehot_db(module, sigmap);
+ onehot_db.verbose = verbose_onehot;
+
+ dict<SigBit, pair<SigSpec, Const>> eqdb;
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type == "$eq")
+ {
+ dict<SigBit, State> bits;
+
+ SigSpec A = sigmap(cell->getPort("\\A"));
+ SigSpec B = sigmap(cell->getPort("\\B"));
+
+ int a_width = cell->getParam("\\A_WIDTH").as_int();
+ int b_width = cell->getParam("\\B_WIDTH").as_int();
+
+ if (a_width < b_width) {
+ bool a_signed = cell->getParam("\\A_SIGNED").as_int();
+ A.extend_u0(b_width, a_signed);
+ }
+
+ if (b_width < a_width) {
+ bool b_signed = cell->getParam("\\B_SIGNED").as_int();
+ B.extend_u0(a_width, b_signed);
+ }
+
+ for (int i = 0; i < GetSize(A); i++) {
+ SigBit a_bit = A[i], b_bit = B[i];
+ if (b_bit.wire && !a_bit.wire) {
+ std::swap(a_bit, b_bit);
+ }
+ if (!a_bit.wire || b_bit.wire)
+ goto next_cell;
+ if (bits.count(a_bit))
+ goto next_cell;
+ bits[a_bit] = b_bit.data;
+ }
+
+ if (GetSize(bits) > 20)
+ goto next_cell;
+
+ bits.sort();
+ pair<SigSpec, Const> entry;
+
+ for (auto it : bits) {
+ entry.first.append_bit(it.first);
+ entry.second.bits.push_back(it.second);
+ }
+
+ eqdb[sigmap(cell->getPort("\\Y")[0])] = entry;
+ goto next_cell;
+ }
+
+ if (cell->type == "$logic_not")
+ {
+ dict<SigBit, State> bits;
+
+ SigSpec A = sigmap(cell->getPort("\\A"));
+
+ for (int i = 0; i < GetSize(A); i++)
+ bits[A[i]] = State::S0;
+
+ bits.sort();
+ pair<SigSpec, Const> entry;
+
+ for (auto it : bits) {
+ entry.first.append_bit(it.first);
+ entry.second.bits.push_back(it.second);
+ }
+
+ eqdb[sigmap(cell->getPort("\\Y")[0])] = entry;
+ goto next_cell;
+ }
+ next_cell:;
+ }
+
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type != "$pmux")
+ continue;
+
+ string src = cell->get_src_attribute();
+ int width = cell->getParam("\\WIDTH").as_int();
+ int width_bits = ceil_log2(width);
+ int extwidth = width;
+
+ while (extwidth & (extwidth-1))
+ extwidth++;
+
+ dict<SigSpec, pool<int>> seldb;
+
+ SigSpec A = cell->getPort("\\A");
+ SigSpec B = cell->getPort("\\B");
+ SigSpec S = sigmap(cell->getPort("\\S"));
+ for (int i = 0; i < GetSize(S); i++)
+ {
+ if (!eqdb.count(S[i]))
+ continue;
+
+ auto &entry = eqdb.at(S[i]);
+ seldb[entry.first].insert(i);
+ }
+
+ if (seldb.empty())
+ continue;
+
+ bool printed_pmux_header = false;
+
+ if (verbose) {
+ printed_pmux_header = true;
+ log("Inspecting $pmux cell %s/%s.\n", log_id(module), log_id(cell));
+ log(" data width: %d (next power-of-2 = %d, log2 = %d)\n", width, extwidth, width_bits);
+ }
+
+ SigSpec updated_S = cell->getPort("\\S");
+ SigSpec updated_B = cell->getPort("\\B");
+
+ while (!seldb.empty())
+ {
+ // pick the largest entry in seldb
+ SigSpec sig = seldb.begin()->first;
+ for (auto &it : seldb) {
+ if (GetSize(sig) < GetSize(it.first))
+ sig = it.first;
+ else if (GetSize(seldb.at(sig)) < GetSize(it.second))
+ sig = it.first;
+ }
+
+ // find the relevant choices
+ bool is_onehot = GetSize(sig) > 2;
+ dict<Const, int> choices;
+ for (int i : seldb.at(sig)) {
+ Const val = eqdb.at(S[i]).second;
+ int onebits = 0;
+ for (auto b : val.bits)
+ if (b == State::S1)
+ onebits++;
+ if (onebits > 1)
+ is_onehot = false;
+ choices[val] = i;
+ }
+
+ bool full_pmux = GetSize(choices) == GetSize(S);
+
+ // TBD: also find choices that are using signals that are subsets of the bits in "sig"
+
+ if (!verbose)
+ {
+ if (is_onehot && !allow_onehot && !optimize_onehot) {
+ seldb.erase(sig);
+ continue;
+ }
+
+ if (GetSize(choices) < min_choices) {
+ seldb.erase(sig);
+ continue;
+ }
+ }
+
+ if (!printed_pmux_header) {
+ printed_pmux_header = true;
+ log("Inspecting $pmux cell %s/%s.\n", log_id(module), log_id(cell));
+ log(" data width: %d (next power-of-2 = %d, log2 = %d)\n", width, extwidth, width_bits);
+ }
+
+ log(" checking ctrl signal %s\n", log_signal(sig));
+
+ auto print_choices = [&]() {
+ log(" table of choices:\n");
+ for (auto &it : choices)
+ log(" %3d: %s: %s\n", it.second, log_signal(it.first),
+ log_signal(B.extract(it.second*width, width)));
+ };
+
+ if (verbose)
+ {
+ if (is_onehot && !allow_onehot && !optimize_onehot) {
+ print_choices();
+ log(" ignoring one-hot encoding.\n");
+ seldb.erase(sig);
+ continue;
+ }
+
+ if (GetSize(choices) < min_choices) {
+ print_choices();
+ log(" insufficient choices.\n");
+ seldb.erase(sig);
+ continue;
+ }
+ }
+
+ if (is_onehot && optimize_onehot)
+ {
+ print_choices();
+ if (!onehot_db.query(sig))
+ {
+ log(" failed to detect onehot driver. do not optimize.\n");
+ }
+ else
+ {
+ log(" optimizing one-hot encoding.\n");
+ for (auto &it : choices)
+ {
+ const Const &val = it.first;
+ int index = -1;
+
+ for (int i = 0; i < GetSize(val); i++)
+ if (val[i] == State::S1) {
+ log_assert(index < 0);
+ index = i;
+ }
+
+ if (index < 0) {
+ log(" %3d: zero encoding.\n", it.second);
+ continue;
+ }
+
+ SigBit new_ctrl = sig[index];
+ log(" %3d: new crtl signal is %s.\n", it.second, log_signal(new_ctrl));
+ updated_S[it.second] = new_ctrl;
+ }
+ }
+ seldb.erase(sig);
+ continue;
+ }
+
+ // find the best permutation
+ vector<int> perm_new_from_old(GetSize(sig));
+ Const perm_xormask(State::S0, GetSize(sig));
+ {
+ vector<int> values(GetSize(choices));
+ vector<bool> used_src_columns(GetSize(sig));
+ vector<vector<bool>> columns(GetSize(sig), vector<bool>(GetSize(values)));
+
+ for (int i = 0; i < GetSize(choices); i++) {
+ Const val = choices.element(i)->first;
+ for (int k = 0; k < GetSize(val); k++)
+ if (val[k] == State::S1)
+ columns[k][i] = true;
+ }
+
+ for (int dst_col = GetSize(sig)-1; dst_col >= 0; dst_col--)
+ {
+ int best_src_col = -1;
+ bool best_inv = false;
+ int best_maxval = 0;
+ int best_delta = 0;
+
+ // find best src column for this dst column
+ for (int src_col = 0; src_col < GetSize(sig); src_col++)
+ {
+ if (used_src_columns[src_col])
+ continue;
+
+ int this_maxval = 0;
+ int this_minval = 1 << 30;
+
+ int this_inv_maxval = 0;
+ int this_inv_minval = 1 << 30;
+
+ for (int i = 0; i < GetSize(values); i++)
+ {
+ int val = values[i];
+ int inv_val = val;
+
+ if (columns[src_col][i])
+ val |= 1 << dst_col;
+ else
+ inv_val |= 1 << dst_col;
+
+ this_maxval = std::max(this_maxval, val);
+ this_minval = std::min(this_minval, val);
+
+ this_inv_maxval = std::max(this_inv_maxval, inv_val);
+ this_inv_minval = std::min(this_inv_minval, inv_val);
+ }
+
+ int this_delta = this_maxval - this_minval;
+ int this_inv_delta = this_maxval - this_minval;
+ bool this_inv = false;
+
+ if (!norange && this_delta != this_inv_delta)
+ this_inv = this_inv_delta < this_delta;
+ else if (this_maxval != this_inv_maxval)
+ this_inv = this_inv_maxval < this_maxval;
+
+ if (this_inv) {
+ this_delta = this_inv_delta;
+ this_maxval = this_inv_maxval;
+ this_minval = this_inv_minval;
+ }
+
+ bool this_is_better = false;
+
+ if (best_src_col < 0)
+ this_is_better = true;
+ else if (!norange && this_delta != best_delta)
+ this_is_better = this_delta < best_delta;
+ else if (this_maxval != best_maxval)
+ this_is_better = this_maxval < best_maxval;
+ else
+ this_is_better = sig[best_src_col] < sig[src_col];
+
+ if (this_is_better) {
+ best_src_col = src_col;
+ best_inv = this_inv;
+ best_maxval = this_maxval;
+ best_delta = this_delta;
+ }
+ }
+
+ used_src_columns[best_src_col] = true;
+ perm_new_from_old[dst_col] = best_src_col;
+ perm_xormask[dst_col] = best_inv ? State::S1 : State::S0;
+ }
+ }
+
+ // permutated sig
+ SigSpec perm_sig(State::S0, GetSize(sig));
+ for (int i = 0; i < GetSize(sig); i++)
+ perm_sig[i] = sig[perm_new_from_old[i]];
+
+ log(" best permutation: %s\n", log_signal(perm_sig));
+ log(" best xor mask: %s\n", log_signal(perm_xormask));
+
+ // permutated choices
+ int min_choice = 1 << 30;
+ int max_choice = -1;
+ dict<Const, int> perm_choices;
+
+ for (auto &it : choices)
+ {
+ Const &old_c = it.first;
+ Const new_c(State::S0, GetSize(old_c));
+
+ for (int i = 0; i < GetSize(old_c); i++)
+ new_c[i] = old_c[perm_new_from_old[i]];
+
+ Const new_c_before_xor = new_c;
+ new_c = const_xor(new_c, perm_xormask, false, false, GetSize(new_c));
+
+ perm_choices[new_c] = it.second;
+
+ min_choice = std::min(min_choice, new_c.as_int());
+ max_choice = std::max(max_choice, new_c.as_int());
+
+ log(" %3d: %s -> %s -> %s: %s\n", it.second, log_signal(old_c), log_signal(new_c_before_xor),
+ log_signal(new_c), log_signal(B.extract(it.second*width, width)));
+ }
+
+ int range_density = 100*GetSize(choices) / (max_choice-min_choice+1);
+ int absolute_density = 100*GetSize(choices) / (max_choice+1);
+
+ log(" choices: %d\n", GetSize(choices));
+ log(" min choice: %d\n", min_choice);
+ log(" max choice: %d\n", max_choice);
+ log(" range density: %d%%\n", range_density);
+ log(" absolute density: %d%%\n", absolute_density);
+
+ if (full_pmux) {
+ int full_density = 100*GetSize(choices) / (1 << GetSize(sig));
+ log(" full density: %d%%\n", full_density);
+ if (full_density < min_density) {
+ full_pmux = false;
+ } else {
+ min_choice = 0;
+ max_choice = (1 << GetSize(sig))-1;
+ log(" update to full case.\n");
+ log(" new min choice: %d\n", min_choice);
+ log(" new max choice: %d\n", max_choice);
+ }
+ }
+
+ bool full_case = (min_choice == 0) && (max_choice == (1 << GetSize(sig))-1) && (full_pmux || max_choice+1 == GetSize(choices));
+ log(" full case: %s\n", full_case ? "true" : "false");
+
+ // check density percentages
+ Const offset(State::S0, GetSize(sig));
+ if (!norange && absolute_density < min_density && range_density >= min_density)
+ {
+ offset = Const(min_choice, GetSize(sig));
+ log(" offset: %s\n", log_signal(offset));
+
+ min_choice -= offset.as_int();
+ max_choice -= offset.as_int();
+
+ dict<Const, int> new_perm_choices;
+ for (auto &it : perm_choices)
+ new_perm_choices[const_sub(it.first, offset, false, false, GetSize(sig))] = it.second;
+ perm_choices.swap(new_perm_choices);
+ } else
+ if (absolute_density < min_density) {
+ log(" insufficient density.\n");
+ seldb.erase(sig);
+ continue;
+ }
+
+ // creat cmp signal
+ SigSpec cmp = perm_sig;
+ if (perm_xormask.as_bool())
+ cmp = module->Xor(NEW_ID, cmp, perm_xormask, false, src);
+ if (offset.as_bool())
+ cmp = module->Sub(NEW_ID, cmp, offset, false, src);
+
+ // create enable signal
+ SigBit en = State::S1;
+ if (!full_case) {
+ Const enable_mask(State::S0, max_choice+1);
+ for (auto &it : perm_choices)
+ enable_mask[it.first.as_int()] = State::S1;
+ en = module->addWire(NEW_ID);
+ module->addShift(NEW_ID, enable_mask, cmp, en, false, src);
+ }
+
+ // create data signal
+ SigSpec data(State::Sx, (max_choice+1)*extwidth);
+ if (full_pmux) {
+ for (int i = 0; i <= max_choice; i++)
+ data.replace(i*extwidth, A);
+ }
+ for (auto &it : perm_choices) {
+ int position = it.first.as_int()*extwidth;
+ int data_index = it.second;
+ data.replace(position, B.extract(data_index*width, width));
+ updated_S[data_index] = State::S0;
+ updated_B.replace(data_index*width, SigSpec(State::Sx, width));
+ }
+
+ // create shiftx cell
+ SigSpec shifted_cmp = {cmp, SigSpec(State::S0, width_bits)};
+ SigSpec outsig = module->addWire(NEW_ID, width);
+ Cell *c = module->addShiftx(NEW_ID, data, shifted_cmp, outsig, false, src);
+ updated_S.append(en);
+ updated_B.append(outsig);
+ log(" created $shiftx cell %s.\n", log_id(c));
+
+ // remove this sig and continue with the next block
+ seldb.erase(sig);
+ }
+
+ // update $pmux cell
+ cell->setPort("\\S", updated_S);
+ cell->setPort("\\B", updated_B);
+ cell->setParam("\\S_WIDTH", GetSize(updated_S));
+ }
+ }
+ }
+} Pmux2ShiftxPass;
+
+struct OnehotPass : public Pass {
+ OnehotPass() : Pass("onehot", "optimize $eq cells for onehot signals") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" onehot [options] [selection]\n");
+ log("\n");
+ log("This pass optimizes $eq cells that compare one-hot signals against constants\n");
+ log("\n");
+ log(" -v, -vv\n");
+ log(" verbose output\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ bool verbose = false;
+ bool verbose_onehot = false;
+
+ log_header(design, "Executing ONEHOT pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-v") {
+ verbose = true;
+ continue;
+ }
+ if (args[argidx] == "-vv") {
+ verbose = true;
+ verbose_onehot = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ OnehotDatabase onehot_db(module, sigmap);
+ onehot_db.verbose = verbose_onehot;
+
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type != "$eq")
+ continue;
+
+ SigSpec A = sigmap(cell->getPort("\\A"));
+ SigSpec B = sigmap(cell->getPort("\\B"));
+
+ int a_width = cell->getParam("\\A_WIDTH").as_int();
+ int b_width = cell->getParam("\\B_WIDTH").as_int();
+
+ if (a_width < b_width) {
+ bool a_signed = cell->getParam("\\A_SIGNED").as_int();
+ A.extend_u0(b_width, a_signed);
+ }
+
+ if (b_width < a_width) {
+ bool b_signed = cell->getParam("\\B_SIGNED").as_int();
+ B.extend_u0(a_width, b_signed);
+ }
+
+ if (A.is_fully_const())
+ std::swap(A, B);
+
+ if (!B.is_fully_const())
+ continue;
+
+ if (verbose)
+ log("Checking $eq(%s, %s) cell %s/%s.\n", log_signal(A), log_signal(B), log_id(module), log_id(cell));
+
+ if (!onehot_db.query(A)) {
+ if (verbose)
+ log(" onehot driver test on %s failed.\n", log_signal(A));
+ continue;
+ }
+
+ int index = -1;
+ bool not_onehot = false;
+
+ for (int i = 0; i < GetSize(B); i++) {
+ if (B[i] != State::S1)
+ continue;
+ if (index >= 0)
+ not_onehot = true;
+ index = i;
+ }
+
+ if (index < 0) {
+ if (verbose)
+ log(" not optimizing the zero pattern.\n");
+ continue;
+ }
+
+ SigSpec Y = cell->getPort("\\Y");
+
+ if (not_onehot)
+ {
+ if (verbose)
+ log(" replacing with constant 0 driver.\n");
+ else
+ log("Replacing one-hot $eq(%s, %s) cell %s/%s with constant 0 driver.\n", log_signal(A), log_signal(B), log_id(module), log_id(cell));
+ module->connect(Y, SigSpec(1, GetSize(Y)));
+ }
+ else
+ {
+ SigSpec sig = A[index];
+ if (verbose)
+ log(" replacing with signal %s.\n", log_signal(sig));
+ else
+ log("Replacing one-hot $eq(%s, %s) cell %s/%s with signal %s.\n",log_signal(A), log_signal(B), log_id(module), log_id(cell), log_signal(sig));
+ sig.extend_u0(GetSize(Y));
+ module->connect(Y, sig);
+ }
+
+ module->remove(cell);
+ }
+ }
+ }
+} OnehotPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/rmports.cc b/passes/opt/rmports.cc
index fc1596eb..32363dd6 100644
--- a/passes/opt/rmports.cc
+++ b/passes/opt/rmports.cc
@@ -171,7 +171,7 @@ struct RmportsPassPass : public Pass {
wire->port_output = false;
wire->port_id = 0;
}
- log("Removed %zu unused ports.\n", unused_ports.size());
+ log("Removed %d unused ports.\n", GetSize(unused_ports));
// Re-number all of the wires that DO have ports still on them
for(size_t i=0; i<module->ports.size(); i++)
diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc
index 52245ce3..1fbc4108 100644
--- a/passes/opt/wreduce.cc
+++ b/passes/opt/wreduce.cc
@@ -29,6 +29,7 @@ PRIVATE_NAMESPACE_BEGIN
struct WreduceConfig
{
pool<IdString> supported_cell_types;
+ bool keepdc = false;
WreduceConfig()
{
@@ -82,7 +83,7 @@ struct WreduceWorker
SigBit ref = sig_a[i];
for (int k = 0; k < GetSize(sig_s); k++) {
- if (ref != Sx && sig_b[k*GetSize(sig_a) + i] != Sx && ref != sig_b[k*GetSize(sig_a) + i])
+ if ((config->keepdc || (ref != Sx && sig_b[k*GetSize(sig_a) + i] != Sx)) && ref != sig_b[k*GetSize(sig_a) + i])
goto no_match_ab;
if (sig_b[k*GetSize(sig_a) + i] != Sx)
ref = sig_b[k*GetSize(sig_a) + i];
@@ -180,6 +181,8 @@ struct WreduceWorker
}
auto info = mi.query(sig_q[i]);
+ if (info == nullptr)
+ return;
if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
remove_init_bits.insert(sig_q[i]);
sig_d.remove(i);
@@ -462,12 +465,10 @@ struct WreduceWorker
SigSpec initsig = init_attr_sigmap(w);
int width = std::min(GetSize(initval), GetSize(initsig));
for (int i = 0; i < width; i++) {
- log_dump(initsig[i], remove_init_bits.count(initsig[i]));
if (!remove_init_bits.count(initsig[i]))
new_initval[i] = initval[i];
}
w->attributes.at("\\init") = new_initval;
- log_dump(w->name, initval, new_initval);
}
}
}
@@ -495,6 +496,9 @@ struct WreducePass : public Pass {
log(" Do not change the width of memory address ports. Use this options in\n");
log(" flows that use the 'memory_memx' pass.\n");
log("\n");
+ log(" -keepdc\n");
+ log(" Do not optimize explicit don't-care values.\n");
+ log("\n");
}
void execute(std::vector<std::string> args, Design *design) YS_OVERRIDE
{
@@ -509,6 +513,10 @@ struct WreducePass : public Pass {
opt_memx = true;
continue;
}
+ if (args[argidx] == "-keepdc") {
+ config.keepdc = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -531,6 +539,42 @@ struct WreducePass : public Pass {
module->connect(sig, Const(0, GetSize(sig)));
}
}
+
+ if (c->type.in("$div", "$mod", "$pow"))
+ {
+ SigSpec A = c->getPort("\\A");
+ int original_a_width = GetSize(A);
+ if (c->getParam("\\A_SIGNED").as_bool()) {
+ while (GetSize(A) > 1 && A[GetSize(A)-1] == State::S0 && A[GetSize(A)-2] == State::S0)
+ A.remove(GetSize(A)-1, 1);
+ } else {
+ while (GetSize(A) > 0 && A[GetSize(A)-1] == State::S0)
+ A.remove(GetSize(A)-1, 1);
+ }
+ if (original_a_width != GetSize(A)) {
+ log("Removed top %d bits (of %d) from port A of cell %s.%s (%s).\n",
+ original_a_width-GetSize(A), original_a_width, log_id(module), log_id(c), log_id(c->type));
+ c->setPort("\\A", A);
+ c->setParam("\\A_WIDTH", GetSize(A));
+ }
+
+ SigSpec B = c->getPort("\\B");
+ int original_b_width = GetSize(B);
+ if (c->getParam("\\B_SIGNED").as_bool()) {
+ while (GetSize(B) > 1 && B[GetSize(B)-1] == State::S0 && B[GetSize(B)-2] == State::S0)
+ B.remove(GetSize(B)-1, 1);
+ } else {
+ while (GetSize(B) > 0 && B[GetSize(B)-1] == State::S0)
+ B.remove(GetSize(B)-1, 1);
+ }
+ if (original_b_width != GetSize(B)) {
+ log("Removed top %d bits (of %d) from port B of cell %s.%s (%s).\n",
+ original_b_width-GetSize(B), original_b_width, log_id(module), log_id(c), log_id(c->type));
+ c->setPort("\\B", B);
+ c->setParam("\\B_WIDTH", GetSize(B));
+ }
+ }
+
if (!opt_memx && c->type.in("$memrd", "$memwr", "$meminit")) {
IdString memid = c->getParam("\\MEMID").decode_string();
RTLIL::Memory *mem = module->memories.at(memid);
diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore
index c9263057..0ad36ea2 100644
--- a/passes/pmgen/.gitignore
+++ b/passes/pmgen/.gitignore
@@ -1 +1,2 @@
/ice40_dsp_pm.h
+/peepopt_pm.h
diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc
index e0609d9b..7911132d 100644
--- a/passes/pmgen/Makefile.inc
+++ b/passes/pmgen/Makefile.inc
@@ -1,8 +1,23 @@
OBJS += passes/pmgen/ice40_dsp.o
+OBJS += passes/pmgen/peepopt.o
+
+# --------------------------------------
passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h
.SECONDARY: passes/pmgen/ice40_dsp_pm.h
passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg
- $(P) mkdir -p passes/pmgen && python3 $^ $@
+ $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p ice40_dsp $(filter-out $<,$^)
+
+# --------------------------------------
+
+passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h
+EXTRA_OBJS += passes/pmgen/peepopt_pm.h
+.SECONDARY: passes/pmgen/peepopt_pm.h
+
+PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul.pmg
+PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
+
+passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
+ $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^)
diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md
index 223b4305..2f0b1fd5 100644
--- a/passes/pmgen/README.md
+++ b/passes/pmgen/README.md
@@ -29,19 +29,25 @@ up in any future matches:
pm.blacklist(some_cell);
-The `.run(callback_function)` method searches for all matches and calls the
-callback function for each found match:
+The `.run_<pattern_name>(callback_function)` method searches for all matches
+for the pattern`<pattern_name>` and calls the callback function for each found
+match:
- pm.run([&](){
+ pm.run_foobar([&](){
log("found matching 'foo' cell: %s\n", log_id(pm.st.foo));
log(" with 'bar' cell: %s\n", log_id(pm.st.bar));
});
The `.pmg` file declares matcher state variables that are accessible via the
-`.st.<state_name>` members. (The `.st` member is of type `foobar_pm::state_t`.)
+`.st_<pattern_name>.<state_name>` members. (The `.st_<pattern_name>` member is
+of type `foobar_pm::state_<pattern_name>_t`.)
Similarly the `.pmg` file declares user data variables that become members of
-`.ud`, a struct of type `foobar_pm::udata_t`.
+`.ud_<pattern_name>`, a struct of type `foobar_pm::udata_<pattern_name>_t`.
+
+There are four versions of the `run_<pattern_name>()` method: Without callback,
+callback without arguments, callback with reference to `pm`, and callback with
+reference to `pm.st_<pattern_name>`.
The .pmg File Format
@@ -52,6 +58,12 @@ lines consist of whitespace-separated tokens.
Lines in `.pmg` files starting with `//` are comments.
+Declaring a pattern
+-------------------
+
+A `.pmg` file contains one or more patterns. Each pattern starts with a line
+with the `pattern` keyword followed by the name of the pattern.
+
Declaring state variables
-------------------------
@@ -66,7 +78,7 @@ State variables are automatically managed by the generated backtracking algorith
and saved and restored as needed.
They are automatically initialized to the default constructed value of their type
-when `.run(callback_function)` is called.
+when `.run_<pattern_name>(callback_function)` is called.
Declaring udata variables
-------------------------
@@ -83,8 +95,8 @@ They are declared like state variables, just using the `udata` statement:
udata <int> min_data_width max_data_width
udata <IdString> data_port_name
-They are atomatically initialzed to the default constructed value of their type
-when ther pattern matcher object is constructed.
+They are automatically initialized to the default constructed value of their type
+when the pattern matcher object is constructed.
Embedded C++ code
-----------------
@@ -158,7 +170,7 @@ Finally, `filter <expression>` narrows down the remaining list of cells. For
performance reasons `filter` statements should only be used for things that
can't be done using `select` and `index`.
-The `optional` statement marks optional matches. I.e. the matcher will also
+The `optional` statement marks optional matches. That is, the matcher will also
explore the case where `mul` is set to `nullptr`. Without the `optional`
statement a match may only be assigned nullptr when one of the `if` expressions
evaluates to `false`.
@@ -220,5 +232,5 @@ But in some cases it is more natural to utilize the implicit branch statement:
portAB = \B;
endcode
-There is an implicit `code..endcode` block at the end of each `.pgm` file
+There is an implicit `code..endcode` block at the end of each `.pmg` file
that just accepts everything that gets all the way there.
diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc
index 3a054a46..39d033a0 100644
--- a/passes/pmgen/ice40_dsp.cc
+++ b/passes/pmgen/ice40_dsp.cc
@@ -19,47 +19,50 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
-#include "passes/pmgen/ice40_dsp_pm.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
+#include "passes/pmgen/ice40_dsp_pm.h"
+
void create_ice40_dsp(ice40_dsp_pm &pm)
{
+ auto &st = pm.st_ice40_dsp;
+
#if 0
log("\n");
- log("ffA: %s\n", log_id(pm.st.ffA, "--"));
- log("ffB: %s\n", log_id(pm.st.ffB, "--"));
- log("mul: %s\n", log_id(pm.st.mul, "--"));
- log("ffY: %s\n", log_id(pm.st.ffY, "--"));
- log("addAB: %s\n", log_id(pm.st.addAB, "--"));
- log("muxAB: %s\n", log_id(pm.st.muxAB, "--"));
- log("ffS: %s\n", log_id(pm.st.ffS, "--"));
+ log("ffA: %s\n", log_id(st.ffA, "--"));
+ log("ffB: %s\n", log_id(st.ffB, "--"));
+ log("mul: %s\n", log_id(st.mul, "--"));
+ log("ffY: %s\n", log_id(st.ffY, "--"));
+ log("addAB: %s\n", log_id(st.addAB, "--"));
+ log("muxAB: %s\n", log_id(st.muxAB, "--"));
+ log("ffS: %s\n", log_id(st.ffS, "--"));
#endif
- log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul));
+ log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul));
- if (GetSize(pm.st.sigA) > 16) {
- log(" input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA));
+ if (GetSize(st.sigA) > 16) {
+ log(" input A (%s) is too large (%d > 16).\n", log_signal(st.sigA), GetSize(st.sigA));
return;
}
- if (GetSize(pm.st.sigB) > 16) {
- log(" input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB));
+ if (GetSize(st.sigB) > 16) {
+ log(" input B (%s) is too large (%d > 16).\n", log_signal(st.sigB), GetSize(st.sigB));
return;
}
- if (GetSize(pm.st.sigS) > 32) {
- log(" accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS));
+ if (GetSize(st.sigS) > 32) {
+ log(" accumulator (%s) is too large (%d > 32).\n", log_signal(st.sigS), GetSize(st.sigS));
return;
}
- if (GetSize(pm.st.sigY) > 32) {
- log(" output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY));
+ if (GetSize(st.sigY) > 32) {
+ log(" output (%s) is too large (%d > 32).\n", log_signal(st.sigY), GetSize(st.sigY));
return;
}
- bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool();
+ bool mul_signed = st.mul->getParam("\\A_SIGNED").as_bool();
if (mul_signed) {
log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n");
@@ -69,21 +72,21 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
log(" replacing $mul with SB_MAC16 cell.\n");
Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
- pm.module->swap_names(cell, pm.st.mul);
+ pm.module->swap_names(cell, st.mul);
// SB_MAC16 Input Interface
- SigSpec A = pm.st.sigA;
+ SigSpec A = st.sigA;
A.extend_u0(16, mul_signed);
- SigSpec B = pm.st.sigB;
+ SigSpec B = st.sigB;
B.extend_u0(16, mul_signed);
SigSpec CD;
- if (pm.st.muxA)
- CD = pm.st.muxA->getPort("\\B");
- if (pm.st.muxB)
- CD = pm.st.muxB->getPort("\\A");
+ if (st.muxA)
+ CD = st.muxA->getPort("\\B");
+ if (st.muxB)
+ CD = st.muxB->getPort("\\A");
CD.extend_u0(32, mul_signed);
cell->setPort("\\A", A);
@@ -91,8 +94,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setPort("\\C", CD.extract(0, 16));
cell->setPort("\\D", CD.extract(16, 16));
- cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0);
- cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0);
+ cell->setParam("\\A_REG", st.ffA ? State::S1 : State::S0);
+ cell->setParam("\\B_REG", st.ffB ? State::S1 : State::S0);
cell->setPort("\\AHOLD", State::S0);
cell->setPort("\\BHOLD", State::S0);
@@ -102,25 +105,25 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setPort("\\IRSTTOP", State::S0);
cell->setPort("\\IRSTBOT", State::S0);
- if (pm.st.clock_vld)
+ if (st.clock_vld)
{
- cell->setPort("\\CLK", pm.st.clock);
+ cell->setPort("\\CLK", st.clock);
cell->setPort("\\CE", State::S1);
- cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1);
+ cell->setParam("\\NEG_TRIGGER", st.clock_pol ? State::S0 : State::S1);
- log(" clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge");
+ log(" clock: %s (%s)", log_signal(st.clock), st.clock_pol ? "posedge" : "negedge");
- if (pm.st.ffA)
- log(" ffA:%s", log_id(pm.st.ffA));
+ if (st.ffA)
+ log(" ffA:%s", log_id(st.ffA));
- if (pm.st.ffB)
- log(" ffB:%s", log_id(pm.st.ffB));
+ if (st.ffB)
+ log(" ffB:%s", log_id(st.ffB));
- if (pm.st.ffY)
- log(" ffY:%s", log_id(pm.st.ffY));
+ if (st.ffY)
+ log(" ffY:%s", log_id(st.ffY));
- if (pm.st.ffS)
- log(" ffS:%s", log_id(pm.st.ffS));
+ if (st.ffS)
+ log(" ffS:%s", log_id(st.ffS));
log("\n");
}
@@ -144,16 +147,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
// SB_MAC16 Output Interface
- SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY;
+ SigSpec O = st.ffS ? st.sigS : st.sigY;
if (GetSize(O) < 32)
O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
cell->setPort("\\O", O);
- if (pm.st.addAB) {
- log(" accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type));
- cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
- cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
+ if (st.addAB) {
+ log(" accumulator %s (%s)\n", log_id(st.addAB), log_id(st.addAB->type));
+ cell->setPort("\\ADDSUBTOP", st.addAB->type == "$add" ? State::S0 : State::S1);
+ cell->setPort("\\ADDSUBBOT", st.addAB->type == "$add" ? State::S0 : State::S1);
} else {
cell->setPort("\\ADDSUBTOP", State::S0);
cell->setPort("\\ADDSUBBOT", State::S0);
@@ -166,10 +169,10 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setPort("\\OHOLDBOT", State::S0);
SigSpec acc_reset = State::S0;
- if (pm.st.muxA)
- acc_reset = pm.st.muxA->getPort("\\S");
- if (pm.st.muxB)
- acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S"));
+ if (st.muxA)
+ acc_reset = st.muxA->getPort("\\S");
+ if (st.muxB)
+ acc_reset = pm.module->Not(NEW_ID, st.muxB->getPort("\\S"));
cell->setPort("\\OLOADTOP", acc_reset);
cell->setPort("\\OLOADBOT", acc_reset);
@@ -179,17 +182,17 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setParam("\\C_REG", State::S0);
cell->setParam("\\D_REG", State::S0);
- cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
- cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
- cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0);
+ cell->setParam("\\TOP_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
+ cell->setParam("\\BOT_8x8_MULT_REG", st.ffY ? State::S1 : State::S0);
+ cell->setParam("\\PIPELINE_16x16_MULT_REG1", st.ffY ? State::S1 : State::S0);
cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0);
- cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
+ cell->setParam("\\TOPOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
- cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
+ cell->setParam("\\BOTOUTPUT_SELECT", Const(st.ffS ? 1 : 3, 2));
cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
@@ -198,9 +201,9 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
- pm.autoremove(pm.st.mul);
- pm.autoremove(pm.st.ffY);
- pm.autoremove(pm.st.ffS);
+ pm.autoremove(st.mul);
+ pm.autoremove(st.ffY);
+ pm.autoremove(st.ffS);
}
struct Ice40DspPass : public Pass {
@@ -230,7 +233,7 @@ struct Ice40DspPass : public Pass {
extra_args(args, argidx, design);
for (auto module : design->selected_modules())
- ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp);
+ ice40_dsp_pm(module, module->selected_cells()).run_ice40_dsp(create_ice40_dsp);
}
} Ice40DspPass;
diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg
index 96c62e31..1f3590d4 100644
--- a/passes/pmgen/ice40_dsp.pmg
+++ b/passes/pmgen/ice40_dsp.pmg
@@ -1,3 +1,5 @@
+pattern ice40_dsp
+
state <SigBit> clock
state <bool> clock_pol clock_vld
state <SigSpec> sigA sigB sigY sigS
diff --git a/passes/pmgen/peepopt.cc b/passes/pmgen/peepopt.cc
new file mode 100644
index 00000000..e7f95cf8
--- /dev/null
+++ b/passes/pmgen/peepopt.cc
@@ -0,0 +1,68 @@
+/*
+ * 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
+
+bool did_something;
+
+#include "passes/pmgen/peepopt_pm.h"
+
+struct PeepoptPass : public Pass {
+ PeepoptPass() : Pass("peepopt", "collection of peephole optimizers") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" peepopt [options] [selection]\n");
+ log("\n");
+ log("This pass applies a collection of peephole optimizers to the current design.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing PEEPOPT pass (run peephole optimizers).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-singleton") {
+ // singleton_mode = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules()) {
+ did_something = true;
+ while (did_something) {
+ did_something = false;
+ peepopt_pm pm(module, module->selected_cells());
+ pm.run_shiftmul();
+ pm.run_muldiv();
+ }
+ }
+ }
+} PeepoptPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/peepopt_muldiv.pmg b/passes/pmgen/peepopt_muldiv.pmg
new file mode 100644
index 00000000..06c27583
--- /dev/null
+++ b/passes/pmgen/peepopt_muldiv.pmg
@@ -0,0 +1,36 @@
+pattern muldiv
+
+state <SigSpec> t x y
+
+match mul
+ select mul->type == $mul
+ select GetSize(port(mul, \A)) + GetSize(port(mul, \B)) <= GetSize(port(mul, \Y))
+endmatch
+
+code t x y
+ t = port(mul, \Y);
+ x = port(mul, \A);
+ y = port(mul, \B);
+ branch;
+ std::swap(x, y);
+endcode
+
+match div
+ select div->type.in($div)
+ index <SigSpec> port(div, \A) === t
+ index <SigSpec> port(div, \B) === x
+endmatch
+
+code
+ SigSpec div_y = port(div, \Y);
+ SigSpec val_y = y;
+
+ if (GetSize(div_y) != GetSize(val_y))
+ val_y.extend_u0(GetSize(div_y), param(div, \A_SIGNED).as_bool());
+
+ did_something = true;
+ log("muldiv pattern in %s: mul=%s, div=%s\n", log_id(module), log_id(mul), log_id(div));
+ module->connect(div_y, val_y);
+ autoremove(div);
+ reject;
+endcode
diff --git a/passes/pmgen/peepopt_shiftmul.pmg b/passes/pmgen/peepopt_shiftmul.pmg
new file mode 100644
index 00000000..6adab4e5
--- /dev/null
+++ b/passes/pmgen/peepopt_shiftmul.pmg
@@ -0,0 +1,94 @@
+pattern shiftmul
+//
+// Optimize mul+shift pairs that result from expressions such as foo[s*W+:W]
+//
+
+state <SigSpec> shamt
+
+match shift
+ select shift->type.in($shift, $shiftx, $shr)
+endmatch
+
+code shamt
+ shamt = port(shift, \B);
+ if (shamt.empty())
+ reject;
+ if (shamt[GetSize(shamt)-1] == State::S0) {
+ do {
+ shamt.remove(GetSize(shamt)-1);
+ if (shamt.empty())
+ reject;
+ } while (shamt[GetSize(shamt)-1] == State::S0);
+ } else
+ if (shift->type.in($shift, $shiftx) && param(shift, \B_SIGNED).as_bool()) {
+ reject;
+ }
+ if (GetSize(shamt) > 20)
+ reject;
+endcode
+
+match mul
+ select mul->type.in($mul)
+ select port(mul, \A).is_fully_const() || port(mul, \B).is_fully_const()
+ index <SigSpec> port(mul, \Y) === shamt
+endmatch
+
+code
+ IdString const_factor_port = port(mul, \A).is_fully_const() ? \A : \B;
+ IdString const_factor_signed = const_factor_port == \A ? \A_SIGNED : \B_SIGNED;
+ Const const_factor_cnst = port(mul, const_factor_port).as_const();
+ int const_factor = const_factor_cnst.as_int();
+
+ if (GetSize(const_factor_cnst) == 0)
+ reject;
+
+ if (const_factor_cnst.bits[GetSize(const_factor_cnst)-1] != State::S0 &&
+ param(mul, const_factor_signed).as_bool())
+ reject;
+
+ if (GetSize(const_factor_cnst) > 20)
+ reject;
+
+ if (GetSize(port(shift, \Y)) > const_factor)
+ reject;
+
+ int factor_bits = ceil_log2(const_factor);
+ SigSpec mul_din = port(mul, const_factor_port == \A ? \B : \A);
+
+ if (GetSize(shamt) < factor_bits+GetSize(mul_din))
+ reject;
+
+ did_something = true;
+ log("shiftmul pattern in %s: shift=%s, mul=%s\n", log_id(module), log_id(shift), log_id(mul));
+
+ int new_const_factor = 1 << factor_bits;
+ SigSpec padding(State::Sx, new_const_factor-const_factor);
+ SigSpec old_a = port(shift, \A), new_a;
+ int trunc = 0;
+
+ if (GetSize(old_a) % const_factor != 0) {
+ trunc = const_factor - GetSize(old_a) % const_factor;
+ old_a.append(SigSpec(State::Sx, trunc));
+ }
+
+ for (int i = 0; i*const_factor < GetSize(old_a); i++) {
+ SigSpec slice = old_a.extract(i*const_factor, const_factor);
+ new_a.append(slice);
+ new_a.append(padding);
+ }
+
+ if (trunc > 0)
+ new_a.remove(GetSize(new_a)-trunc, trunc);
+
+ SigSpec new_b = {mul_din, SigSpec(State::S0, factor_bits)};
+ if (param(shift, \B_SIGNED).as_bool())
+ new_b.append(State::S0);
+
+ shift->setPort(\A, new_a);
+ shift->setParam(\A_WIDTH, GetSize(new_a));
+ shift->setPort(\B, new_b);
+ shift->setParam(\B_WIDTH, GetSize(new_b));
+
+ blacklist(shift);
+ reject;
+endcode
diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py
index d9747b06..81052afc 100644
--- a/passes/pmgen/pmgen.py
+++ b/passes/pmgen/pmgen.py
@@ -3,15 +3,42 @@
import re
import sys
import pprint
+import getopt
pp = pprint.PrettyPrinter(indent=4)
-pmgfile = sys.argv[1]
-assert pmgfile.endswith(".pmg")
-prefix = pmgfile[0:-4]
-prefix = prefix.split('/')[-1]
-outfile = sys.argv[2]
-
+prefix = None
+pmgfiles = list()
+outfile = None
+debug = False
+genhdr = False
+
+opts, args = getopt.getopt(sys.argv[1:], "p:o:dg")
+
+for o, a in opts:
+ if o == "-p":
+ prefix = a
+ elif o == "-o":
+ outfile = a
+ elif o == "-d":
+ debug = True
+ elif o == "-g":
+ genhdr = True
+
+if outfile is None:
+ outfile = "/dev/stdout"
+
+for a in args:
+ assert a.endswith(".pmg")
+ if prefix is None and len(args) == 1:
+ prefix = a[0:-4]
+ prefix = prefix.split('/')[-1]
+ pmgfiles.append(a)
+
+assert prefix is not None
+
+current_pattern = None
+patterns = dict()
state_types = dict()
udata_types = dict()
blocks = list()
@@ -77,7 +104,8 @@ def rewrite_cpp(s):
return "".join(t)
-with open(pmgfile, "r") as f:
+def process_pmgfile(f):
+ global current_pattern
while True:
line = f.readline()
if line == "": break
@@ -87,14 +115,31 @@ with open(pmgfile, "r") as f:
if len(cmd) == 0 or cmd[0].startswith("//"): continue
cmd = cmd[0]
+ if cmd == "pattern":
+ if current_pattern is not None:
+ block = dict()
+ block["type"] = "final"
+ block["pattern"] = current_pattern
+ blocks.append(block)
+ line = line.split()
+ assert len(line) == 2
+ assert line[1] not in patterns
+ current_pattern = line[1]
+ patterns[current_pattern] = len(blocks)
+ state_types[current_pattern] = dict()
+ udata_types[current_pattern] = dict()
+ continue
+
+ assert current_pattern is not None
+
if cmd == "state":
m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
assert m
type_str = m.group(1)
states_str = m.group(2)
for s in re.split(r"\s+", states_str):
- assert s not in state_types
- state_types[s] = type_str
+ assert s not in state_types[current_pattern]
+ state_types[current_pattern][s] = type_str
continue
if cmd == "udata":
@@ -103,19 +148,20 @@ with open(pmgfile, "r") as f:
type_str = m.group(1)
udatas_str = m.group(2)
for s in re.split(r"\s+", udatas_str):
- assert s not in udata_types
- udata_types[s] = type_str
+ assert s not in udata_types[current_pattern]
+ udata_types[current_pattern][s] = type_str
continue
if cmd == "match":
block = dict()
block["type"] = "match"
+ block["pattern"] = current_pattern
line = line.split()
assert len(line) == 2
- assert line[1] not in state_types
+ assert line[1] not in state_types[current_pattern]
block["cell"] = line[1]
- state_types[line[1]] = "Cell*";
+ state_types[current_pattern][line[1]] = "Cell*";
block["if"] = list()
block["select"] = list()
@@ -158,15 +204,18 @@ with open(pmgfile, "r") as f:
assert False
blocks.append(block)
+ continue
if cmd == "code":
block = dict()
block["type"] = "code"
+ block["pattern"] = current_pattern
+
block["code"] = list()
block["states"] = set()
for s in line.split()[1:]:
- assert s in state_types
+ assert s in state_types[current_pattern]
block["states"].add(s)
while True:
@@ -179,18 +228,37 @@ with open(pmgfile, "r") as f:
block["code"].append(rewrite_cpp(l.rstrip()))
blocks.append(block)
+ continue
-with open(outfile, "w") as f:
- print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f)
- print("", file=f)
+ assert False
- print("#include \"kernel/yosys.h\"", file=f)
- print("#include \"kernel/sigtools.h\"", file=f)
- print("", file=f)
+for fn in pmgfiles:
+ with open(fn, "r") as f:
+ process_pmgfile(f)
+
+if current_pattern is not None:
+ block = dict()
+ block["type"] = "final"
+ block["pattern"] = current_pattern
+ blocks.append(block)
+
+current_pattern = None
+
+if debug:
+ pp.pprint(blocks)
- print("YOSYS_NAMESPACE_BEGIN", file=f)
+with open(outfile, "w") as f:
+ for fn in pmgfiles:
+ print("// Generated by pmgen.py from {}".format(fn), file=f)
print("", file=f)
+ if genhdr:
+ print("#include \"kernel/yosys.h\"", file=f)
+ print("#include \"kernel/sigtools.h\"", file=f)
+ print("", file=f)
+ print("YOSYS_NAMESPACE_BEGIN", file=f)
+ print("", file=f)
+
print("struct {}_pm {{".format(prefix), file=f)
print(" Module *module;", file=f)
print(" SigMap sigmap;", file=f)
@@ -212,17 +280,19 @@ with open(outfile, "w") as f:
print(" int rollback;", file=f)
print("", file=f)
- print(" struct state_t {", file=f)
- for s, t in sorted(state_types.items()):
- print(" {} {};".format(t, s), file=f)
- print(" } st;", file=f)
- print("", file=f)
+ for current_pattern in sorted(patterns.keys()):
+ print(" struct state_{}_t {{".format(current_pattern), file=f)
+ for s, t in sorted(state_types[current_pattern].items()):
+ print(" {} {};".format(t, s), file=f)
+ print(" }} st_{};".format(current_pattern), file=f)
+ print("", file=f)
- print(" struct udata_t {", file=f)
- for s, t in sorted(udata_types.items()):
- print(" {} {};".format(t, s), file=f)
- print(" } ud;", file=f)
- print("", file=f)
+ print(" struct udata_{}_t {{".format(current_pattern), file=f)
+ for s, t in sorted(udata_types[current_pattern].items()):
+ print(" {} {};".format(t, s), file=f)
+ print(" }} ud_{};".format(current_pattern), file=f)
+ print("", file=f)
+ current_pattern = None
for v, n in sorted(ids.items()):
if n[0] == "\\":
@@ -258,20 +328,24 @@ with open(outfile, "w") as f:
print(" }", file=f)
print("", file=f)
- print(" void check_blacklist() {", file=f)
- print(" if (!blacklist_dirty)", file=f)
- print(" return;", file=f)
- print(" blacklist_dirty = false;", file=f)
- for index in range(len(blocks)):
- block = blocks[index]
- if block["type"] == "match":
- print(" if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f)
- print(" rollback = {};".format(index+1), file=f)
- print(" return;", file=f)
- print(" }", file=f)
- print(" rollback = 0;", file=f)
- print(" }", file=f)
- print("", file=f)
+ for current_pattern in sorted(patterns.keys()):
+ print(" void check_blacklist_{}() {{".format(current_pattern), file=f)
+ print(" if (!blacklist_dirty)", file=f)
+ print(" return;", file=f)
+ print(" blacklist_dirty = false;", file=f)
+ for index in range(len(blocks)):
+ block = blocks[index]
+ if block["pattern"] != current_pattern:
+ continue
+ if block["type"] == "match":
+ print(" if (st_{}.{} != nullptr && blacklist_cells.count(st_{}.{})) {{".format(current_pattern, block["cell"], current_pattern, block["cell"]), file=f)
+ print(" rollback = {};".format(index+1), file=f)
+ print(" return;", file=f)
+ print(" }", file=f)
+ print(" rollback = 0;", file=f)
+ print(" }", file=f)
+ print("", file=f)
+ current_pattern = None
print(" SigSpec port(Cell *cell, IdString portname) {", file=f)
print(" return sigmap(cell->getPort(portname));", file=f)
@@ -294,11 +368,13 @@ with open(outfile, "w") as f:
print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f)
print(" module(module), sigmap(module) {", file=f)
- for s, t in sorted(udata_types.items()):
- if t.endswith("*"):
- print(" ud.{} = nullptr;".format(s), file=f)
- else:
- print(" ud.{} = {}();".format(s, t), file=f)
+ for current_pattern in sorted(patterns.keys()):
+ for s, t in sorted(udata_types[current_pattern].items()):
+ if t.endswith("*"):
+ print(" ud_{}.{} = nullptr;".format(current_pattern,s), file=f)
+ else:
+ print(" ud_{}.{} = {}();".format(current_pattern, s, t), file=f)
+ current_pattern = None
print(" for (auto cell : module->cells()) {", file=f)
print(" for (auto &conn : cell->connections())", file=f)
print(" add_siguser(conn.second, cell);", file=f)
@@ -328,34 +404,52 @@ with open(outfile, "w") as f:
print(" }", file=f)
print("", file=f)
- print(" void run(std::function<void()> on_accept_f) {", file=f)
- print(" on_accept = on_accept_f;", file=f)
- print(" rollback = 0;", file=f)
- print(" blacklist_dirty = false;", file=f)
- for s, t in sorted(state_types.items()):
- if t.endswith("*"):
- print(" st.{} = nullptr;".format(s), file=f)
- else:
- print(" st.{} = {}();".format(s, t), file=f)
- print(" block_0();", file=f)
- print(" }", file=f)
- print("", file=f)
-
- print(" void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f)
- print(" run([&](){on_accept_f(*this);});", file=f)
- print(" }", file=f)
- print("", file=f)
+ for current_pattern in sorted(patterns.keys()):
+ print(" void run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f)
+ print(" on_accept = on_accept_f;", file=f)
+ print(" rollback = 0;", file=f)
+ print(" blacklist_dirty = false;", file=f)
+ for s, t in sorted(state_types[current_pattern].items()):
+ if t.endswith("*"):
+ print(" st_{}.{} = nullptr;".format(current_pattern, s), file=f)
+ else:
+ print(" st_{}.{} = {}();".format(current_pattern, s, t), file=f)
+ print(" block_{}();".format(patterns[current_pattern]), file=f)
+ print(" }", file=f)
+ print("", file=f)
+ print(" void run_{}(std::function<void({}_pm&)> on_accept_f) {{".format(current_pattern, prefix), file=f)
+ print(" run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f)
+ print(" }", file=f)
+ print("", file=f)
+ print(" void run_{}(std::function<void(state_{}_t&)> on_accept_f) {{".format(current_pattern, current_pattern), file=f)
+ print(" run_{}([&](){{on_accept_f(st_{});}});".format(current_pattern, current_pattern), file=f)
+ print(" }", file=f)
+ print("", file=f)
+ print(" void run_{}() {{".format(current_pattern), file=f)
+ print(" run_{}([](){{}});".format(current_pattern, current_pattern), file=f)
+ print(" }", file=f)
+ print("", file=f)
+ current_pattern = None
for index in range(len(blocks)):
block = blocks[index]
print(" void block_{}() {{".format(index), file=f)
+ current_pattern = block["pattern"]
+
+ if block["type"] == "final":
+ print(" on_accept();", file=f)
+ print(" check_blacklist_{}();".format(current_pattern), file=f)
+ print(" }", file=f)
+ if index+1 != len(blocks):
+ print("", file=f)
+ continue
const_st = set()
nonconst_st = set()
restore_st = set()
- for i in range(index):
+ for i in range(patterns[current_pattern], index):
if blocks[i]["type"] == "code":
for s in blocks[i]["states"]:
const_st.add(s)
@@ -378,27 +472,27 @@ with open(outfile, "w") as f:
assert False
for s in sorted(const_st):
- t = state_types[s]
+ t = state_types[current_pattern][s]
if t.endswith("*"):
- print(" {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+ print(" {} const &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
else:
- print(" const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+ print(" const {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
for s in sorted(nonconst_st):
- t = state_types[s]
- print(" {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+ t = state_types[current_pattern][s]
+ print(" {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
if len(restore_st):
print("", file=f)
for s in sorted(restore_st):
- t = state_types[s]
+ t = state_types[current_pattern][s]
print(" {} backup_{} = {};".format(t, s, s), file=f)
if block["type"] == "code":
print("", file=f)
print(" do {", file=f)
- print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f)
- print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f)
+ print("#define reject do {{ check_blacklist_{}(); goto rollback_label; }} while(0)".format(current_pattern), file=f)
+ print("#define accept do {{ on_accept(); check_blacklist_{}(); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f)
print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
for line in block["code"]:
@@ -417,11 +511,11 @@ with open(outfile, "w") as f:
if len(restore_st) or len(nonconst_st):
print("", file=f)
for s in sorted(restore_st):
- t = state_types[s]
+ t = state_types[current_pattern][s]
print(" {} = backup_{};".format(s, s), file=f)
for s in sorted(nonconst_st):
if s not in restore_st:
- t = state_types[s]
+ t = state_types[current_pattern][s]
if t.endswith("*"):
print(" {} = nullptr;".format(s), file=f)
else:
@@ -470,17 +564,12 @@ with open(outfile, "w") as f:
else:
assert False
-
+ current_pattern = None
print(" }", file=f)
print("", file=f)
- print(" void block_{}() {{".format(len(blocks)), file=f)
- print(" on_accept();", file=f)
- print(" check_blacklist();", file=f)
- print(" }", file=f)
print("};", file=f)
- print("", file=f)
- print("YOSYS_NAMESPACE_END", file=f)
-
-# pp.pprint(blocks)
+ if genhdr:
+ print("", file=f)
+ print("YOSYS_NAMESPACE_END", file=f)
diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc
index 1329c1fe..d029282f 100644
--- a/passes/proc/proc_mux.cc
+++ b/passes/proc/proc_mux.cc
@@ -108,6 +108,7 @@ struct SigSnippets
struct SnippetSwCache
{
+ dict<RTLIL::SwitchRule*, pool<RTLIL::SigBit>, hash_ptr_ops> full_case_bits_cache;
dict<RTLIL::SwitchRule*, pool<int>, hash_ptr_ops> cache;
const SigSnippets *snippets;
int current_snippet;
@@ -143,7 +144,13 @@ struct SnippetSwCache
}
};
-RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw, bool ifxmode)
+void apply_attrs(RTLIL::Cell *cell, const RTLIL::SwitchRule *sw, const RTLIL::CaseRule *cs)
+{
+ cell->attributes = sw->attributes;
+ cell->add_strpool_attribute("\\src", cs->get_strpool_attribute("\\src"));
+}
+
+RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode)
{
std::stringstream sstr;
sstr << "$procmux$" << (autoidx++);
@@ -172,7 +179,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
{
// create compare cell
RTLIL::Cell *eq_cell = mod->addCell(stringf("%s_CMP%d", sstr.str().c_str(), cmp_wire->width), ifxmode ? "$eqx" : "$eq");
- eq_cell->attributes = sw->attributes;
+ apply_attrs(eq_cell, sw, cs);
eq_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
eq_cell->parameters["\\B_SIGNED"] = RTLIL::Const(0);
@@ -198,7 +205,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
// reduce cmp vector to one logic signal
RTLIL::Cell *any_cell = mod->addCell(sstr.str() + "_ANY", "$reduce_or");
- any_cell->attributes = sw->attributes;
+ apply_attrs(any_cell, sw, cs);
any_cell->parameters["\\A_SIGNED"] = RTLIL::Const(0);
any_cell->parameters["\\A_WIDTH"] = RTLIL::Const(cmp_wire->width);
@@ -211,7 +218,7 @@ RTLIL::SigSpec gen_cmp(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
return RTLIL::SigSpec(ctrl_wire);
}
-RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode)
+RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::SigSpec else_signal, RTLIL::Cell *&last_mux_cell, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode)
{
log_assert(when_signal.size() == else_signal.size());
@@ -223,7 +230,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
return when_signal;
// compare results
- RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode);
+ RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, cs, ifxmode);
if (ctrl_sig.size() == 0)
return when_signal;
log_assert(ctrl_sig.size() == 1);
@@ -233,7 +240,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
// create the multiplexer itself
RTLIL::Cell *mux_cell = mod->addCell(sstr.str(), "$mux");
- mux_cell->attributes = sw->attributes;
+ apply_attrs(mux_cell, sw, cs);
mux_cell->parameters["\\WIDTH"] = RTLIL::Const(when_signal.size());
mux_cell->setPort("\\A", else_signal);
@@ -245,7 +252,7 @@ RTLIL::SigSpec gen_mux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const s
return RTLIL::SigSpec(result_wire);
}
-void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, bool ifxmode)
+void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::vector<RTLIL::SigSpec> &compare, RTLIL::SigSpec when_signal, RTLIL::Cell *last_mux_cell, RTLIL::SwitchRule *sw, RTLIL::CaseRule *cs, bool ifxmode)
{
log_assert(last_mux_cell != NULL);
log_assert(when_signal.size() == last_mux_cell->getPort("\\A").size());
@@ -253,7 +260,7 @@ void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::ve
if (when_signal == last_mux_cell->getPort("\\A"))
return;
- RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, ifxmode);
+ RTLIL::SigSpec ctrl_sig = gen_cmp(mod, signal, compare, sw, cs, ifxmode);
log_assert(ctrl_sig.size() == 1);
last_mux_cell->type = "$pmux";
@@ -268,6 +275,49 @@ void append_pmux(RTLIL::Module *mod, const RTLIL::SigSpec &signal, const std::ve
last_mux_cell->parameters["\\S_WIDTH"] = last_mux_cell->getPort("\\S").size();
}
+const pool<SigBit> &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRule *sw)
+{
+ if (!swcache.full_case_bits_cache.count(sw))
+ {
+ pool<SigBit> bits;
+
+ if (sw->get_bool_attribute("\\full_case"))
+ {
+ bool first_case = true;
+
+ for (auto cs : sw->cases)
+ {
+ pool<SigBit> case_bits;
+
+ for (auto it : cs->actions) {
+ for (auto bit : it.first)
+ case_bits.insert(bit);
+ }
+
+ for (auto it : cs->switches) {
+ for (auto bit : get_full_case_bits(swcache, it))
+ case_bits.insert(bit);
+ }
+
+ if (first_case) {
+ first_case = false;
+ bits = case_bits;
+ } else {
+ pool<SigBit> new_bits;
+ for (auto bit : bits)
+ if (case_bits.count(bit))
+ new_bits.insert(bit);
+ bits.swap(new_bits);
+ }
+ }
+ }
+
+ bits.swap(swcache.full_case_bits_cache[sw]);
+ }
+
+ return swcache.full_case_bits_cache.at(sw);
+}
+
RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> &swpara,
RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode)
{
@@ -337,6 +387,12 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d
}
}
+ // mask default bits that are irrelevant because the output is driven by a full case
+ const pool<SigBit> &full_case_bits = get_full_case_bits(swcache, sw);
+ for (int i = 0; i < GetSize(sig); i++)
+ if (full_case_bits.count(sig[i]))
+ result[i] = State::Sx;
+
// evaluate in reverse order to give the first entry the top priority
RTLIL::SigSpec initial_val = result;
RTLIL::Cell *last_mux_cell = NULL;
@@ -345,9 +401,9 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d
RTLIL::CaseRule *cs2 = sw->cases[case_idx];
RTLIL::SigSpec value = signal_to_mux_tree(mod, swcache, swpara, cs2, sig, initial_val, ifxmode);
if (last_mux_cell && pgroups[case_idx] == pgroups[case_idx+1])
- append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, ifxmode);
+ append_pmux(mod, sw->signal, cs2->compare, value, last_mux_cell, sw, cs2, ifxmode);
else
- result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, ifxmode);
+ result = gen_mux(mod, sw->signal, cs2->compare, value, result, last_mux_cell, sw, cs2, ifxmode);
}
}
diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc
index 7c334e66..4f40be44 100644
--- a/passes/proc/proc_rmdead.cc
+++ b/passes/proc/proc_rmdead.cc
@@ -28,7 +28,7 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-void proc_rmdead(RTLIL::SwitchRule *sw, int &counter)
+void proc_rmdead(RTLIL::SwitchRule *sw, int &counter, int &full_case_counter)
{
BitPatternPool pool(sw->signal);
@@ -56,11 +56,16 @@ void proc_rmdead(RTLIL::SwitchRule *sw, int &counter)
}
for (auto switch_it : sw->cases[i]->switches)
- proc_rmdead(switch_it, counter);
+ proc_rmdead(switch_it, counter, full_case_counter);
if (is_default)
pool.take_all();
}
+
+ if (pool.empty() && !sw->get_bool_attribute("\\full_case")) {
+ sw->set_bool_attribute("\\full_case");
+ full_case_counter++;
+ }
}
struct ProcRmdeadPass : public Pass {
@@ -87,12 +92,15 @@ struct ProcRmdeadPass : public Pass {
for (auto &proc_it : mod->processes) {
if (!design->selected(mod, proc_it.second))
continue;
- int counter = 0;
+ int counter = 0, full_case_counter = 0;
for (auto switch_it : proc_it.second->root_case.switches)
- proc_rmdead(switch_it, counter);
+ proc_rmdead(switch_it, counter, full_case_counter);
if (counter > 0)
log("Removed %d dead cases from process %s in module %s.\n", counter,
- proc_it.first.c_str(), log_id(mod));
+ log_id(proc_it.first), log_id(mod));
+ if (full_case_counter > 0)
+ log("Marked %d switch rules as full_case in process %s in module %s.\n",
+ full_case_counter, log_id(proc_it.first), log_id(mod));
total_counter += counter;
}
}
diff --git a/passes/sat/assertpmux.cc b/passes/sat/assertpmux.cc
index 509cb0ba..3b432c46 100644
--- a/passes/sat/assertpmux.cc
+++ b/passes/sat/assertpmux.cc
@@ -180,7 +180,7 @@ struct AssertpmuxWorker
};
struct AssertpmuxPass : public Pass {
- AssertpmuxPass() : Pass("assertpmux", "convert internal signals to module ports") { }
+ AssertpmuxPass() : Pass("assertpmux", "adds asserts for parallel muxes") { }
void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
@@ -195,8 +195,8 @@ struct AssertpmuxPass : public Pass {
log("\n");
log(" -always\n");
log(" usually the $pmux condition is only checked when the $pmux output\n");
- log(" is used be the mux tree it drives. this option will deactivate this\n");
- log(" additional constrained and check the $pmux condition always.\n");
+ log(" is used by the mux tree it drives. this option will deactivate this\n");
+ log(" additional constraint and check the $pmux condition always.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc
index 048aec7f..b4549bc3 100644
--- a/passes/sat/cutpoint.cc
+++ b/passes/sat/cutpoint.cc
@@ -24,7 +24,7 @@ USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct CutpointPass : public Pass {
- CutpointPass() : Pass("cutpoint", "add hi/lo cover cells for each wire bit") { }
+ CutpointPass() : Pass("cutpoint", "adds formal cut points to the design") { }
void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc
index 80934548..71ce1683 100644
--- a/passes/sat/expose.cc
+++ b/passes/sat/expose.cc
@@ -508,7 +508,7 @@ struct ExposePass : public Pass {
}
for (auto &conn : module->connections_)
- conn.first = out_to_in_map(sigmap(conn.first));
+ conn.first = out_to_in_map(conn.first);
}
if (flag_cut)
diff --git a/passes/sat/fmcombine.cc b/passes/sat/fmcombine.cc
index cd75ca86..00c09854 100644
--- a/passes/sat/fmcombine.cc
+++ b/passes/sat/fmcombine.cc
@@ -26,6 +26,8 @@ PRIVATE_NAMESPACE_BEGIN
struct opts_t
{
+ bool initeq = false;
+ bool anyeq = false;
bool fwd = false;
bool bwd = false;
bool nop = false;
@@ -56,7 +58,7 @@ struct FmcombineWorker
return newsig;
}
- void import_prim_cell(Cell *cell, const string &suffix)
+ Cell *import_prim_cell(Cell *cell, const string &suffix)
{
Cell *c = module->addCell(cell->name.str() + suffix, cell->type);
c->parameters = cell->parameters;
@@ -64,6 +66,8 @@ struct FmcombineWorker
for (auto &conn : cell->connections())
c->setPort(conn.first, import_sig(conn.second, suffix));
+
+ return c;
}
void import_hier_cell(Cell *cell)
@@ -102,8 +106,24 @@ struct FmcombineWorker
for (auto cell : original->cells()) {
if (design->module(cell->type) == nullptr) {
- import_prim_cell(cell, "_gold");
- import_prim_cell(cell, "_gate");
+ if (opts.anyeq && cell->type.in("$anyseq", "$anyconst")) {
+ Cell *gold = import_prim_cell(cell, "_gold");
+ for (auto &conn : cell->connections())
+ module->connect(import_sig(conn.second, "_gate"), gold->getPort(conn.first));
+ } else {
+ Cell *gold = import_prim_cell(cell, "_gold");
+ Cell *gate = import_prim_cell(cell, "_gate");
+ if (opts.initeq) {
+ if (cell->type.in("$ff", "$dff", "$dffe",
+ "$dffsr", "$adff", "$dlatch", "$dlatchsr")) {
+ SigSpec gold_q = gold->getPort("\\Q");
+ SigSpec gate_q = gate->getPort("\\Q");
+ SigSpec en = module->Initstate(NEW_ID);
+ SigSpec eq = module->Eq(NEW_ID, gold_q, gate_q);
+ module->addAssume(NEW_ID, eq, en);
+ }
+ }
+ }
} else {
import_hier_cell(cell);
}
@@ -229,6 +249,13 @@ struct FmcombinePass : public Pass {
log("This is useful for formal test benches that check what differences in behavior\n");
log("a slight difference in input causes in a module.\n");
log("\n");
+ log(" -initeq\n");
+ log(" Insert assumptions that initially all FFs in both circuits have the\n");
+ log(" same initial values.\n");
+ log("\n");
+ log(" -anyeq\n");
+ log(" Do not duplicate $anyseq/$anyconst cells.\n");
+ log("\n");
log(" -fwd\n");
log(" Insert forward hint assumptions into the combined module.\n");
log("\n");
@@ -261,6 +288,14 @@ struct FmcombinePass : public Pass {
// filename = args[++argidx];
// continue;
// }
+ if (args[argidx] == "-initeq") {
+ opts.initeq = true;
+ continue;
+ }
+ if (args[argidx] == "-anyeq") {
+ opts.anyeq = true;
+ continue;
+ }
if (args[argidx] == "-fwd") {
opts.fwd = true;
continue;
@@ -297,7 +332,7 @@ struct FmcombinePass : public Pass {
gate_cell = module->cell(gate_name);
if (gate_cell == nullptr)
- log_cmd_error("Gold cell %s not found in module %s.\n", log_id(gate_name), log_id(module));
+ log_cmd_error("Gate cell %s not found in module %s.\n", log_id(gate_name), log_id(module));
}
else
{
@@ -316,7 +351,7 @@ struct FmcombinePass : public Pass {
if (!gold_cell->parameters.empty())
log_cmd_error("Gold cell has unresolved instance parameters.\n");
if (!gate_cell->parameters.empty())
- log_cmd_error("Gold cell has unresolved instance parameters.\n");
+ log_cmd_error("Gate cell has unresolved instance parameters.\n");
FmcombineWorker worker(design, gold_cell->type, opts);
worker.generate();
diff --git a/passes/sat/miter.cc b/passes/sat/miter.cc
index d37f1b12..1a886af7 100644
--- a/passes/sat/miter.cc
+++ b/passes/sat/miter.cc
@@ -254,7 +254,7 @@ void create_miter_equiv(struct Pass *that, std::vector<std::string> args, RTLIL:
if (flag_flatten) {
log_push();
- Pass::call_on_module(design, miter_module, "flatten; opt_expr -keepdc -undriven;;");
+ Pass::call_on_module(design, miter_module, "flatten -wb; opt_expr -keepdc -undriven;;");
log_pop();
}
}
@@ -308,7 +308,7 @@ void create_miter_assert(struct Pass *that, std::vector<std::string> args, RTLIL
if (flag_flatten) {
log_push();
- Pass::call_on_module(design, module, "flatten;;");
+ Pass::call_on_module(design, module, "flatten -wb;;");
log_pop();
}
@@ -385,7 +385,7 @@ struct MiterPass : public Pass {
log(" also create an 'assert' cell that checks if trigger is always low.\n");
log("\n");
log(" -flatten\n");
- log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n");
+ log(" call 'flatten -wb; opt_expr -keepdc -undriven;;' on the miter circuit.\n");
log("\n");
log("\n");
log(" miter -assert [options] module [miter_name]\n");
@@ -399,7 +399,7 @@ struct MiterPass : public Pass {
log(" keep module output ports.\n");
log("\n");
log(" -flatten\n");
- log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n");
+ log(" call 'flatten -wb; opt_expr -keepdc -undriven;;' on the miter circuit.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
diff --git a/passes/sat/mutate.cc b/passes/sat/mutate.cc
index c50678c5..b53bbfeb 100644
--- a/passes/sat/mutate.cc
+++ b/passes/sat/mutate.cc
@@ -934,6 +934,32 @@ struct MutatePass : public Pass {
return;
}
+ if (opts.module.empty())
+ log_cmd_error("Missing -module argument.\n");
+
+ Module *module = design->module(opts.module);
+ if (module == nullptr)
+ log_cmd_error("Module %s not found.\n", log_id(opts.module));
+
+ if (opts.cell.empty())
+ log_cmd_error("Missing -cell argument.\n");
+
+ Cell *cell = module->cell(opts.cell);
+ if (cell == nullptr)
+ log_cmd_error("Cell %s not found in module %s.\n", log_id(opts.cell), log_id(opts.module));
+
+ if (opts.port.empty())
+ log_cmd_error("Missing -port argument.\n");
+
+ if (!cell->hasPort(opts.port))
+ log_cmd_error("Port %s not found on cell %s.%s.\n", log_id(opts.port), log_id(opts.module), log_id(opts.cell));
+
+ if (opts.portbit < 0)
+ log_cmd_error("Missing -portbit argument.\n");
+
+ if (GetSize(cell->getPort(opts.port)) <= opts.portbit)
+ log_cmd_error("Out-of-range -portbit argument for port %s on cell %s.%s.\n", log_id(opts.port), log_id(opts.module), log_id(opts.cell));
+
if (opts.mode == "inv") {
mutate_inv(design, opts);
return;
@@ -944,6 +970,12 @@ struct MutatePass : public Pass {
return;
}
+ if (opts.ctrlbit < 0)
+ log_cmd_error("Missing -ctrlbit argument.\n");
+
+ if (GetSize(cell->getPort(opts.port)) <= opts.ctrlbit)
+ log_cmd_error("Out-of-range -ctrlbit argument for port %s on cell %s.%s.\n", log_id(opts.port), log_id(opts.module), log_id(opts.cell));
+
if (opts.mode == "cnot0" || opts.mode == "cnot1") {
mutate_cnot(design, opts, opts.mode == "cnot1");
return;
diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc
index 695a03e1..e4654d83 100644
--- a/passes/sat/sat.cc
+++ b/passes/sat/sat.cc
@@ -659,6 +659,7 @@ struct SatHelper
void dump_model_to_vcd(std::string vcd_file_name)
{
+ rewrite_filename(vcd_file_name);
FILE *f = fopen(vcd_file_name.c_str(), "w");
if (!f)
log_cmd_error("Can't open output file `%s' for writing: %s\n", vcd_file_name.c_str(), strerror(errno));
@@ -761,6 +762,7 @@ struct SatHelper
void dump_model_to_json(std::string json_file_name)
{
+ rewrite_filename(json_file_name);
FILE *f = fopen(json_file_name.c_str(), "w");
if (!f)
log_cmd_error("Can't open output file `%s' for writing: %s\n", json_file_name.c_str(), strerror(errno));
@@ -1169,6 +1171,7 @@ struct SatPass : public Pass {
if (args[argidx] == "-tempinduct-def") {
tempinduct = true;
tempinduct_def = true;
+ enable_undef = true;
continue;
}
if (args[argidx] == "-tempinduct-baseonly") {
@@ -1504,6 +1507,7 @@ struct SatPass : public Pass {
{
if (!cnf_file_name.empty())
{
+ rewrite_filename(cnf_file_name);
FILE *f = fopen(cnf_file_name.c_str(), "w");
if (!f)
log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno));
@@ -1607,6 +1611,7 @@ struct SatPass : public Pass {
if (!cnf_file_name.empty())
{
+ rewrite_filename(cnf_file_name);
FILE *f = fopen(cnf_file_name.c_str(), "w");
if (!f)
log_cmd_error("Can't open output file `%s' for writing: %s\n", cnf_file_name.c_str(), strerror(errno));
diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc
index 53e248ad..4c3022c7 100644
--- a/passes/sat/sim.cc
+++ b/passes/sat/sim.cc
@@ -88,6 +88,8 @@ struct SimInstance
SimInstance(SimShared *shared, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) :
shared(shared), module(module), instance(instance), parent(parent), sigmap(module)
{
+ log_assert(module);
+
if (parent) {
log_assert(parent->children.count(instance) == 0);
parent->children[instance] = this;
@@ -848,6 +850,9 @@ struct SimPass : public Pass {
if (design->full_selection()) {
top_mod = design->top_module();
+
+ if (!top_mod)
+ log_cmd_error("Design has no top module, use the 'hierarchy' command to specify one.\n");
} else {
auto mods = design->selected_whole_modules();
if (GetSize(mods) != 1)
diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc
index 21b70f49..bf05c68e 100644
--- a/passes/techmap/abc.cc
+++ b/passes/techmap/abc.cc
@@ -29,17 +29,17 @@
// Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558-562, doi:10.1145/368996.369025
// http://en.wikipedia.org/wiki/Topological_sorting
-#define ABC_COMMAND_LIB "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put"
-#define ABC_COMMAND_CTR "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put; buffer; upsize {D}; dnsize {D}; stime -p"
-#define ABC_COMMAND_LUT "strash; ifraig; scorr; dc2; dretime; strash; dch -f; if; mfs2"
-#define ABC_COMMAND_SOP "strash; ifraig; scorr; dc2; dretime; strash; dch -f; cover {I} {P}"
-#define ABC_COMMAND_DFL "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put"
-
-#define ABC_FAST_COMMAND_LIB "strash; dretime; map {D}"
-#define ABC_FAST_COMMAND_CTR "strash; dretime; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
-#define ABC_FAST_COMMAND_LUT "strash; dretime; if"
-#define ABC_FAST_COMMAND_SOP "strash; dretime; cover -I {I} -P {P}"
-#define ABC_FAST_COMMAND_DFL "strash; dretime; map"
+#define ABC_COMMAND_LIB "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; &get -n; &dch -f; &nf {D}; &put"
+#define ABC_COMMAND_CTR "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; &get -n; &dch -f; &nf {D}; &put; buffer; upsize {D}; dnsize {D}; stime -p"
+#define ABC_COMMAND_LUT "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; dch -f; if; mfs2"
+#define ABC_COMMAND_SOP "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; dch -f; cover {I} {P}"
+#define ABC_COMMAND_DFL "strash; ifraig; scorr; dc2; dretime; retime {D}; strash; &get -n; &dch -f; &nf {D}; &put"
+
+#define ABC_FAST_COMMAND_LIB "strash; dretime; retime {D}; map {D}"
+#define ABC_FAST_COMMAND_CTR "strash; dretime; retime {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
+#define ABC_FAST_COMMAND_LUT "strash; dretime; retime {D}; if"
+#define ABC_FAST_COMMAND_SOP "strash; dretime; retime {D}; cover -I {I} -P {P}"
+#define ABC_FAST_COMMAND_DFL "strash; dretime; retime {D}; map"
#include "kernel/register.h"
#include "kernel/sigtools.h"
@@ -49,6 +49,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <cctype>
#include <cerrno>
#include <sstream>
#include <climits>
@@ -330,20 +331,37 @@ void extract_cell(RTLIL::Cell *cell, bool keepff)
std::string remap_name(RTLIL::IdString abc_name, RTLIL::Wire **orig_wire = nullptr)
{
std::string abc_sname = abc_name.substr(1);
- if (abc_sname.substr(0, 5) == "ys__n") {
- int sid = std::stoi(abc_sname.substr(5));
- bool inv = abc_sname.back() == 'v';
- for (auto sig : signal_list) {
- if (sig.id == sid && sig.bit.wire != nullptr) {
- std::stringstream sstr;
- sstr << "$abc$" << map_autoidx << "$" << sig.bit.wire->name.substr(1);
- if (sig.bit.wire->width != 1)
- sstr << "[" << sig.bit.offset << "]";
- if (inv)
- sstr << "_inv";
- if (orig_wire != nullptr)
- *orig_wire = sig.bit.wire;
- return sstr.str();
+ bool isnew = false;
+ if (abc_sname.substr(0, 4) == "new_")
+ {
+ abc_sname.erase(0, 4);
+ isnew = true;
+ }
+ if (abc_sname.substr(0, 5) == "ys__n")
+ {
+ abc_sname.erase(0, 5);
+ if (std::isdigit(abc_sname.at(0)))
+ {
+ int sid = std::stoi(abc_sname);
+ size_t postfix_start = abc_sname.find_first_not_of("0123456789");
+ std::string postfix = postfix_start != std::string::npos ? abc_sname.substr(postfix_start) : "";
+
+ if (sid < GetSize(signal_list))
+ {
+ auto sig = signal_list.at(sid);
+ if (sig.bit.wire != nullptr)
+ {
+ std::stringstream sstr;
+ sstr << "$abc$" << map_autoidx << "$" << sig.bit.wire->name.substr(1);
+ if (sig.bit.wire->width != 1)
+ sstr << "[" << sig.bit.offset << "]";
+ if (isnew)
+ sstr << "_new";
+ sstr << postfix;
+ if (orig_wire != nullptr)
+ *orig_wire = sig.bit.wire;
+ return sstr.str();
+ }
}
}
}
@@ -731,10 +749,6 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
else
abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL;
- if (script_file.empty() && !delay_target.empty())
- for (size_t pos = abc_script.find("dretime;"); pos != std::string::npos; pos = abc_script.find("dretime;", pos+1))
- abc_script = abc_script.substr(0, pos) + "dretime; retime -o {D};" + abc_script.substr(pos+8);
-
for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos))
abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3);
@@ -1726,7 +1740,7 @@ struct AbcPass : public Pass {
signal_init[initsig[i]] = State::S0;
break;
case State::S1:
- signal_init[initsig[i]] = State::S0;
+ signal_init[initsig[i]] = State::S1;
break;
default:
break;
diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc
index 0b5576b0..aa48e112 100644
--- a/passes/techmap/attrmap.cc
+++ b/passes/techmap/attrmap.cc
@@ -111,9 +111,10 @@ struct AttrmapMap : AttrmapAction {
};
struct AttrmapRemove : AttrmapAction {
+ bool has_value;
string name, value;
bool apply(IdString &id, Const &val) YS_OVERRIDE {
- return !(match_name(name, id) && match_value(value, val));
+ return !(match_name(name, id) && (!has_value || match_value(value, val)));
}
};
@@ -235,6 +236,7 @@ struct AttrmapPass : public Pass {
}
auto action = new AttrmapRemove;
action->name = arg1;
+ action->has_value = (p != string::npos);
action->value = val1;
actions.push_back(std::unique_ptr<AttrmapAction>(action));
continue;
diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc
index 48390488..0ad33dc0 100644
--- a/passes/techmap/dffinit.cc
+++ b/passes/techmap/dffinit.cc
@@ -102,7 +102,8 @@ struct DffinitPass : public Pass {
if (wire->attributes.count("\\init")) {
Const value = wire->attributes.at("\\init");
for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
- init_bits[sigmap(SigBit(wire, i))] = value[i];
+ if (value[i] != State::Sx)
+ init_bits[sigmap(SigBit(wire, i))] = value[i];
}
if (wire->port_output)
for (auto bit : sigmap(wire))
diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc
index 274177a6..b5c0498d 100644
--- a/passes/techmap/dfflibmap.cc
+++ b/passes/techmap/dfflibmap.cc
@@ -664,7 +664,7 @@ struct DfflibmapPass : public Pass {
logmap_all();
for (auto &it : design->modules_)
- if (design->selected(it.second) && !it.second->get_bool_attribute("\\blackbox"))
+ if (design->selected(it.second) && !it.second->get_blackbox_attribute())
dfflibmap(design, it.second, prepare_mode);
cell_mappings.clear();
diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc
index 0b7931e4..96d0df5f 100644
--- a/passes/techmap/flowmap.cc
+++ b/passes/techmap/flowmap.cc
@@ -397,7 +397,6 @@ struct FlowGraph
pool<RTLIL::SigBit> x, xi;
NodePrime source_prime = {source, true};
- NodePrime sink_prime = {sink, false};
pool<NodePrime> visited;
vector<NodePrime> worklist = {source_prime};
while (!worklist.empty())
@@ -784,7 +783,7 @@ struct FlowmapWorker
int depth = 0;
for (auto label : labels)
depth = max(depth, label.second);
- log("Mapped to %zu LUTs with maximum depth %d.\n", lut_nodes.size(), depth);
+ log("Mapped to %d LUTs with maximum depth %d.\n", GetSize(lut_nodes), depth);
if (debug)
{
@@ -1196,7 +1195,7 @@ struct FlowmapWorker
bool relax_depth_for_bound(bool first, int depth_bound, dict<RTLIL::SigBit, pool<RTLIL::SigBit>> &lut_critical_outputs)
{
- size_t initial_count = lut_nodes.size();
+ int initial_count = GetSize(lut_nodes);
for (auto node : lut_nodes)
{
@@ -1216,7 +1215,7 @@ struct FlowmapWorker
if (potentials.empty())
{
- log(" Relaxed to %zu (+%zu) LUTs.\n", lut_nodes.size(), lut_nodes.size() - initial_count);
+ log(" Relaxed to %d (+%d) LUTs.\n", GetSize(lut_nodes), GetSize(lut_nodes) - initial_count);
if (!first && break_num == 1)
{
log(" Design fully relaxed.\n");
@@ -1382,7 +1381,8 @@ struct FlowmapWorker
vector<RTLIL::SigBit> input_nodes(lut_edges_bw[node].begin(), lut_edges_bw[node].end());
RTLIL::Const lut_table(State::Sx, max(1 << input_nodes.size(), 1 << minlut));
- for (unsigned i = 0; i < (1 << input_nodes.size()); i++)
+ unsigned const mask = 1 << input_nodes.size();
+ for (unsigned i = 0; i < mask; i++)
{
ce.push();
for (size_t n = 0; n < input_nodes.size(); n++)
@@ -1419,9 +1419,9 @@ struct FlowmapWorker
lut_area += lut_table.size();
if ((int)input_nodes.size() >= minlut)
- log(" Packed into a %zu-LUT %s.%s.\n", input_nodes.size(), log_id(module), log_id(lut));
+ log(" Packed into a %d-LUT %s.%s.\n", GetSize(input_nodes), log_id(module), log_id(lut));
else
- log(" Packed into a %zu-LUT %s.%s (implemented as %d-LUT).\n", input_nodes.size(), log_id(module), log_id(lut), minlut);
+ log(" Packed into a %d-LUT %s.%s (implemented as %d-LUT).\n", GetSize(input_nodes), log_id(module), log_id(lut), minlut);
}
for (auto node : mapped_nodes)
diff --git a/passes/techmap/libparse.cc b/passes/techmap/libparse.cc
index 991cc449..349ccc11 100644
--- a/passes/techmap/libparse.cc
+++ b/passes/techmap/libparse.cc
@@ -94,7 +94,7 @@ int LibertyParser::lexer(std::string &str)
// search for identifiers, numbers, plus or minus.
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.') {
- str = c;
+ str = static_cast<char>(c);
while (1) {
c = f.get();
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.')
diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc
index 12da9ed0..c84cfc39 100644
--- a/passes/techmap/muxcover.cc
+++ b/passes/techmap/muxcover.cc
@@ -23,6 +23,7 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
+#define COST_DMUX 90
#define COST_MUX2 100
#define COST_MUX4 220
#define COST_MUX8 460
@@ -57,6 +58,13 @@ struct MuxcoverWorker
bool use_mux8;
bool use_mux16;
bool nodecode;
+ bool nopartial;
+
+ int cost_dmux;
+ int cost_mux2;
+ int cost_mux4;
+ int cost_mux8;
+ int cost_mux16;
MuxcoverWorker(Module *module) : module(module), sigmap(module)
{
@@ -64,9 +72,32 @@ struct MuxcoverWorker
use_mux8 = false;
use_mux16 = false;
nodecode = false;
+ nopartial = false;
+ cost_dmux = COST_DMUX;
+ cost_mux2 = COST_MUX2;
+ cost_mux4 = COST_MUX4;
+ cost_mux8 = COST_MUX8;
+ cost_mux16 = COST_MUX16;
decode_mux_counter = 0;
}
+ bool xcmp(std::initializer_list<SigBit> list)
+ {
+ auto cursor = list.begin(), end = list.end();
+ log_assert(cursor != end);
+ SigBit tmp = *(cursor++);
+ while (cursor != end) {
+ SigBit bit = *(cursor++);
+ if (bit == State::Sx)
+ continue;
+ if (tmp == State::Sx)
+ tmp = bit;
+ if (bit != tmp)
+ return false;
+ }
+ return true;
+ }
+
void treeify()
{
pool<SigBit> roots;
@@ -124,13 +155,22 @@ struct MuxcoverWorker
log(" Finished treeification: Found %d trees.\n", GetSize(tree_list));
}
- bool follow_muxtree(SigBit &ret_bit, tree_t &tree, SigBit bit, const char *path)
+ bool follow_muxtree(SigBit &ret_bit, tree_t &tree, SigBit bit, const char *path, bool first_layer = true)
{
if (*path) {
- if (tree.muxes.count(bit) == 0)
- return false;
+ if (tree.muxes.count(bit) == 0) {
+ if (first_layer || nopartial)
+ return false;
+ while (path[0] && path[1])
+ path++;
+ if (path[0] == 'S')
+ ret_bit = State::Sx;
+ else
+ ret_bit = bit;
+ return true;
+ }
char port_name[3] = {'\\', *path, 0};
- return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(port_name)), path+1);
+ return follow_muxtree(ret_bit, tree, sigmap(tree.muxes.at(bit)->getPort(port_name)), path+1, false);
} else {
ret_bit = bit;
return true;
@@ -139,7 +179,7 @@ struct MuxcoverWorker
int prepare_decode_mux(SigBit &A, SigBit B, SigBit sel, SigBit bit)
{
- if (A == B)
+ if (A == B || sel == State::Sx)
return 0;
tuple<SigBit, SigBit, SigBit> key(A, B, sel);
@@ -157,7 +197,10 @@ struct MuxcoverWorker
if (std::get<2>(entry))
return 0;
- return COST_MUX2 / GetSize(std::get<1>(entry));
+ if (A == State::Sx || B == State::Sx)
+ return 0;
+
+ return cost_dmux / GetSize(std::get<1>(entry));
}
void implement_decode_mux(SigBit ctrl_bit)
@@ -174,9 +217,32 @@ struct MuxcoverWorker
implement_decode_mux(std::get<0>(key));
implement_decode_mux(std::get<1>(key));
- module->addMuxGate(NEW_ID, std::get<0>(key), std::get<1>(key), std::get<2>(key), ctrl_bit);
+ if (std::get<0>(key) == State::Sx) {
+ module->addBufGate(NEW_ID, std::get<1>(key), ctrl_bit);
+ } else if (std::get<1>(key) == State::Sx) {
+ module->addBufGate(NEW_ID, std::get<0>(key), ctrl_bit);
+ } else {
+ module->addMuxGate(NEW_ID, std::get<0>(key), std::get<1>(key), std::get<2>(key), ctrl_bit);
+ decode_mux_counter++;
+ }
std::get<2>(entry) = true;
- decode_mux_counter++;
+ }
+
+ void find_best_covers(tree_t &tree, const vector<SigBit> &bits)
+ {
+ for (auto bit : bits)
+ find_best_cover(tree, bit);
+ }
+
+ int sum_best_covers(tree_t &tree, const vector<SigBit> &bits)
+ {
+ int sum = 0;
+ for (auto bit : pool<SigBit>(bits.begin(), bits.end())) {
+ int cost = tree.newmuxes.at(bit).cost;
+ log_debug(" Best cost for %s: %d\n", log_signal(bit), cost);
+ sum += cost;
+ }
+ return sum;
}
int find_best_cover(tree_t &tree, SigBit bit)
@@ -209,9 +275,13 @@ struct MuxcoverWorker
mux.inputs.push_back(B);
mux.selects.push_back(S1);
- mux.cost += COST_MUX2;
- mux.cost += find_best_cover(tree, A);
- mux.cost += find_best_cover(tree, B);
+ find_best_covers(tree, mux.inputs);
+ log_debug(" Decode cost for mux2 at %s: %d\n", log_signal(bit), mux.cost);
+
+ mux.cost += cost_mux2;
+ mux.cost += sum_best_covers(tree, mux.inputs);
+
+ log_debug(" Cost of mux2 at %s: %d\n", log_signal(bit), mux.cost);
best_mux = mux;
}
@@ -229,7 +299,7 @@ struct MuxcoverWorker
ok = ok && follow_muxtree(S2, tree, bit, "BS");
if (nodecode)
- ok = ok && S1 == S2;
+ ok = ok && xcmp({S1, S2});
ok = ok && follow_muxtree(T1, tree, bit, "S");
@@ -247,13 +317,15 @@ struct MuxcoverWorker
mux.selects.push_back(S1);
mux.selects.push_back(T1);
- mux.cost += COST_MUX4;
- mux.cost += find_best_cover(tree, A);
- mux.cost += find_best_cover(tree, B);
- mux.cost += find_best_cover(tree, C);
- mux.cost += find_best_cover(tree, D);
+ find_best_covers(tree, mux.inputs);
+ log_debug(" Decode cost for mux4 at %s: %d\n", log_signal(bit), mux.cost);
+
+ mux.cost += cost_mux4;
+ mux.cost += sum_best_covers(tree, mux.inputs);
+
+ log_debug(" Cost of mux4 at %s: %d\n", log_signal(bit), mux.cost);
- if (best_mux.cost > mux.cost)
+ if (best_mux.cost >= mux.cost)
best_mux = mux;
}
}
@@ -277,13 +349,13 @@ struct MuxcoverWorker
ok = ok && follow_muxtree(S4, tree, bit, "BBS");
if (nodecode)
- ok = ok && S1 == S2 && S2 == S3 && S3 == S4;
+ ok = ok && xcmp({S1, S2, S3, S4});
ok = ok && follow_muxtree(T1, tree, bit, "AS");
ok = ok && follow_muxtree(T2, tree, bit, "BS");
if (nodecode)
- ok = ok && T1 == T2;
+ ok = ok && xcmp({T1, T2});
ok = ok && follow_muxtree(U1, tree, bit, "S");
@@ -310,17 +382,15 @@ struct MuxcoverWorker
mux.selects.push_back(T1);
mux.selects.push_back(U1);
- mux.cost += COST_MUX8;
- mux.cost += find_best_cover(tree, A);
- mux.cost += find_best_cover(tree, B);
- mux.cost += find_best_cover(tree, C);
- mux.cost += find_best_cover(tree, D);
- mux.cost += find_best_cover(tree, E);
- mux.cost += find_best_cover(tree, F);
- mux.cost += find_best_cover(tree, G);
- mux.cost += find_best_cover(tree, H);
-
- if (best_mux.cost > mux.cost)
+ find_best_covers(tree, mux.inputs);
+ log_debug(" Decode cost for mux8 at %s: %d\n", log_signal(bit), mux.cost);
+
+ mux.cost += cost_mux8;
+ mux.cost += sum_best_covers(tree, mux.inputs);
+
+ log_debug(" Cost of mux8 at %s: %d\n", log_signal(bit), mux.cost);
+
+ if (best_mux.cost >= mux.cost)
best_mux = mux;
}
}
@@ -356,7 +426,7 @@ struct MuxcoverWorker
ok = ok && follow_muxtree(S8, tree, bit, "BBBS");
if (nodecode)
- ok = ok && S1 == S2 && S2 == S3 && S3 == S4 && S4 == S5 && S5 == S6 && S6 == S7 && S7 == S8;
+ ok = ok && xcmp({S1, S2, S3, S4, S5, S6, S7, S8});
ok = ok && follow_muxtree(T1, tree, bit, "AAS");
ok = ok && follow_muxtree(T2, tree, bit, "ABS");
@@ -364,13 +434,13 @@ struct MuxcoverWorker
ok = ok && follow_muxtree(T4, tree, bit, "BBS");
if (nodecode)
- ok = ok && T1 == T2 && T2 == T3 && T3 == T4;
+ ok = ok && xcmp({T1, T2, T3, T4});
ok = ok && follow_muxtree(U1, tree, bit, "AS");
ok = ok && follow_muxtree(U2, tree, bit, "BS");
if (nodecode)
- ok = ok && U1 == U2;
+ ok = ok && xcmp({U1, U2});
ok = ok && follow_muxtree(V1, tree, bit, "S");
@@ -414,25 +484,15 @@ struct MuxcoverWorker
mux.selects.push_back(U1);
mux.selects.push_back(V1);
- mux.cost += COST_MUX16;
- mux.cost += find_best_cover(tree, A);
- mux.cost += find_best_cover(tree, B);
- mux.cost += find_best_cover(tree, C);
- mux.cost += find_best_cover(tree, D);
- mux.cost += find_best_cover(tree, E);
- mux.cost += find_best_cover(tree, F);
- mux.cost += find_best_cover(tree, G);
- mux.cost += find_best_cover(tree, H);
- mux.cost += find_best_cover(tree, I);
- mux.cost += find_best_cover(tree, J);
- mux.cost += find_best_cover(tree, K);
- mux.cost += find_best_cover(tree, L);
- mux.cost += find_best_cover(tree, M);
- mux.cost += find_best_cover(tree, N);
- mux.cost += find_best_cover(tree, O);
- mux.cost += find_best_cover(tree, P);
-
- if (best_mux.cost > mux.cost)
+ find_best_covers(tree, mux.inputs);
+ log_debug(" Decode cost for mux16 at %s: %d\n", log_signal(bit), mux.cost);
+
+ mux.cost += cost_mux16;
+ mux.cost += sum_best_covers(tree, mux.inputs);
+
+ log_debug(" Cost of mux16 at %s: %d\n", log_signal(bit), mux.cost);
+
+ if (best_mux.cost >= mux.cost)
best_mux = mux;
}
}
@@ -528,6 +588,7 @@ struct MuxcoverWorker
void treecover(tree_t &tree)
{
int count_muxes_by_type[4] = {0, 0, 0, 0};
+ log_debug(" Searching for best cover for tree at %s.\n", log_signal(tree.root));
find_best_cover(tree, tree.root);
implement_best_cover(tree, tree.root, count_muxes_by_type);
log(" Replaced tree at %s: %d MUX2, %d MUX4, %d MUX8, %d MUX16\n", log_signal(tree.root),
@@ -544,12 +605,13 @@ struct MuxcoverWorker
log(" Covering trees:\n");
- // pre-fill cache of decoder muxes
- if (!nodecode)
+ if (!nodecode) {
+ log_debug(" Populating cache of decoder muxes.\n");
for (auto &tree : tree_list) {
find_best_cover(tree, tree.root);
tree.newmuxes.clear();
}
+ }
for (auto &tree : tree_list)
treecover(tree);
@@ -569,15 +631,25 @@ struct MuxcoverPass : public Pass {
log("\n");
log("Cover trees of $_MUX_ cells with $_MUX{4,8,16}_ cells\n");
log("\n");
- log(" -mux4, -mux8, -mux16\n");
- log(" Use the specified types of MUXes. If none of those options are used,\n");
- log(" the effect is the same as if all of them where used.\n");
+ log(" -mux4[=cost], -mux8[=cost], -mux16[=cost]\n");
+ log(" Use the specified types of MUXes (with optional integer costs). If none\n");
+ log(" of these options are given, the effect is the same as if all of them are.\n");
+ log(" Default costs: $_MUX_ = %d, $_MUX4_ = %d,\n", COST_MUX2, COST_MUX4);
+ log(" $_MUX8_ = %d, $_MUX16_ = %d\n", COST_MUX8, COST_MUX16);
+ log("\n");
+ log(" -dmux=cost\n");
+ log(" Use the specified cost for $_MUX_ cells used in decoders.\n");
+ log(" Default cost: %d\n", COST_DMUX);
log("\n");
log(" -nodecode\n");
log(" Do not insert decoder logic. This reduces the number of possible\n");
log(" substitutions, but guarantees that the resulting circuit is not\n");
log(" less efficient than the original circuit.\n");
log("\n");
+ log(" -nopartial\n");
+ log(" Do not consider mappings that use $_MUX<N>_ to select from less\n");
+ log(" than <N> different signals.\n");
+ log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
@@ -587,26 +659,52 @@ struct MuxcoverPass : public Pass {
bool use_mux8 = false;
bool use_mux16 = false;
bool nodecode = false;
+ bool nopartial = false;
+ int cost_dmux = COST_DMUX;
+ int cost_mux4 = COST_MUX4;
+ int cost_mux8 = COST_MUX8;
+ int cost_mux16 = COST_MUX16;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
- if (args[argidx] == "-mux4") {
+ const auto &arg = args[argidx];
+ if (arg.size() >= 5 && arg.substr(0,5) == "-mux4") {
use_mux4 = true;
+ if (arg.size() > 5) {
+ if (arg[5] != '=') break;
+ cost_mux4 = atoi(arg.substr(6).c_str());
+ }
continue;
}
- if (args[argidx] == "-mux8") {
+ if (arg.size() >= 5 && arg.substr(0,5) == "-mux8") {
use_mux8 = true;
+ if (arg.size() > 5) {
+ if (arg[5] != '=') break;
+ cost_mux8 = atoi(arg.substr(6).c_str());
+ }
continue;
}
- if (args[argidx] == "-mux16") {
+ if (arg.size() >= 6 && arg.substr(0,6) == "-mux16") {
use_mux16 = true;
+ if (arg.size() > 6) {
+ if (arg[6] != '=') break;
+ cost_mux16 = atoi(arg.substr(7).c_str());
+ }
+ continue;
+ }
+ if (arg.size() >= 6 && arg.substr(0,6) == "-dmux=") {
+ cost_dmux = atoi(arg.substr(6).c_str());
continue;
}
- if (args[argidx] == "-nodecode") {
+ if (arg == "-nodecode") {
nodecode = true;
continue;
}
+ if (arg == "-nopartial") {
+ nopartial = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -623,7 +721,12 @@ struct MuxcoverPass : public Pass {
worker.use_mux4 = use_mux4;
worker.use_mux8 = use_mux8;
worker.use_mux16 = use_mux16;
+ worker.cost_dmux = cost_dmux;
+ worker.cost_mux4 = cost_mux4;
+ worker.cost_mux8 = cost_mux8;
+ worker.cost_mux16 = cost_mux16;
worker.nodecode = nodecode;
+ worker.nopartial = nopartial;
worker.run();
}
}
diff --git a/passes/techmap/pmuxtree.cc b/passes/techmap/pmuxtree.cc
index b7a22dc3..6a923f48 100644
--- a/passes/techmap/pmuxtree.cc
+++ b/passes/techmap/pmuxtree.cc
@@ -71,9 +71,9 @@ struct PmuxtreePass : public Pass {
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" pmuxtree [options] [selection]\n");
+ log(" pmuxtree [selection]\n");
log("\n");
- log("This pass transforms $pmux cells to a trees of $mux cells.\n");
+ log("This pass transforms $pmux cells to trees of $mux cells.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc
index f20863ba..004ab1eb 100644
--- a/passes/techmap/shregmap.cc
+++ b/passes/techmap/shregmap.cc
@@ -26,7 +26,9 @@ PRIVATE_NAMESPACE_BEGIN
struct ShregmapTech
{
virtual ~ShregmapTech() { }
- virtual bool analyze(vector<int> &taps) = 0;
+ virtual void init(const Module * /*module*/, const SigMap &/*sigmap*/) {}
+ virtual void non_chain_user(const SigBit &/*bit*/, const Cell* /*cell*/, IdString /*port*/) {}
+ virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) = 0;
virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;
};
@@ -54,7 +56,7 @@ struct ShregmapOptions
struct ShregmapTechGreenpak4 : ShregmapTech
{
- bool analyze(vector<int> &taps)
+ bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/)
{
if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
taps.clear();
@@ -91,6 +93,155 @@ struct ShregmapTechGreenpak4 : ShregmapTech
}
};
+struct ShregmapTechXilinx7 : ShregmapTech
+{
+ dict<SigBit, std::tuple<Cell*,int,int>> sigbit_to_shiftx_offset;
+ const ShregmapOptions &opts;
+
+ ShregmapTechXilinx7(const ShregmapOptions &opts) : opts(opts) {}
+
+ virtual void init(const Module* module, const SigMap &sigmap) override
+ {
+ for (const auto &i : module->cells_) {
+ auto cell = i.second;
+ if (cell->type == "$shiftx") {
+ if (cell->getParam("\\Y_WIDTH") != 1) continue;
+ int j = 0;
+ for (auto bit : sigmap(cell->getPort("\\A")))
+ sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, j++, 0);
+ log_assert(j == cell->getParam("\\A_WIDTH").as_int());
+ }
+ else if (cell->type == "$mux") {
+ int j = 0;
+ for (auto bit : sigmap(cell->getPort("\\A")))
+ sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 0, j++);
+ j = 0;
+ for (auto bit : sigmap(cell->getPort("\\B")))
+ sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 1, j++);
+ }
+ }
+ }
+
+ virtual void non_chain_user(const SigBit &bit, const Cell *cell, IdString port) override
+ {
+ auto it = sigbit_to_shiftx_offset.find(bit);
+ if (it == sigbit_to_shiftx_offset.end())
+ return;
+ if (cell) {
+ if (cell->type == "$shiftx" && port == "\\A")
+ return;
+ if (cell->type == "$mux" && (port == "\\A" || port == "\\B"))
+ return;
+ }
+ sigbit_to_shiftx_offset.erase(it);
+ }
+
+ virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) override
+ {
+ if (GetSize(taps) == 1)
+ return taps[0] >= opts.minlen-1 && sigbit_to_shiftx_offset.count(qbits[0]);
+
+ if (taps.back() < opts.minlen-1)
+ return false;
+
+ Cell *shiftx = nullptr;
+ int group = 0;
+ for (int i = 0; i < GetSize(taps); ++i) {
+ auto it = sigbit_to_shiftx_offset.find(qbits[i]);
+ if (it == sigbit_to_shiftx_offset.end())
+ return false;
+
+ // Check taps are sequential
+ if (i != taps[i])
+ return false;
+ // Check taps are not connected to a shift register,
+ // or sequential to the same shift register
+ if (i == 0) {
+ int offset;
+ std::tie(shiftx,offset,group) = it->second;
+ if (offset != i)
+ return false;
+ }
+ else {
+ Cell *shiftx_ = std::get<0>(it->second);
+ if (shiftx_ != shiftx)
+ return false;
+ int offset = std::get<1>(it->second);
+ if (offset != i)
+ return false;
+ int group_ = std::get<2>(it->second);
+ if (group_ != group)
+ return false;
+ }
+ }
+ log_assert(shiftx);
+
+ // Only map if $shiftx exclusively covers the shift register
+ if (shiftx->type == "$shiftx") {
+ if (GetSize(taps) > shiftx->getParam("\\A_WIDTH").as_int())
+ return false;
+ // Due to padding the most significant bits of A may be 1'bx,
+ // and if so, discount them
+ if (GetSize(taps) < shiftx->getParam("\\A_WIDTH").as_int()) {
+ const SigSpec A = shiftx->getPort("\\A");
+ const int A_width = shiftx->getParam("\\A_WIDTH").as_int();
+ for (int i = GetSize(taps); i < A_width; ++i)
+ if (A[i] != RTLIL::Sx) return false;
+ }
+ else if (GetSize(taps) != shiftx->getParam("\\A_WIDTH").as_int())
+ return false;
+ }
+ else if (shiftx->type == "$mux") {
+ if (GetSize(taps) != 2)
+ return false;
+ }
+ else log_abort();
+
+ return true;
+ }
+
+ virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) override
+ {
+ const auto &tap = *taps.begin();
+ auto bit = tap.second;
+
+ auto it = sigbit_to_shiftx_offset.find(bit);
+ log_assert(it != sigbit_to_shiftx_offset.end());
+
+ auto newcell = cell->module->addCell(NEW_ID, "$__XILINX_SHREG_");
+ newcell->set_src_attribute(cell->get_src_attribute());
+ newcell->setParam("\\DEPTH", cell->getParam("\\DEPTH"));
+ newcell->setParam("\\INIT", cell->getParam("\\INIT"));
+ newcell->setParam("\\CLKPOL", cell->getParam("\\CLKPOL"));
+ newcell->setParam("\\ENPOL", cell->getParam("\\ENPOL"));
+
+ newcell->setPort("\\C", cell->getPort("\\C"));
+ newcell->setPort("\\D", cell->getPort("\\D"));
+ if (cell->hasPort("\\E"))
+ newcell->setPort("\\E", cell->getPort("\\E"));
+
+ Cell* shiftx = std::get<0>(it->second);
+ RTLIL::SigSpec l_wire, q_wire;
+ if (shiftx->type == "$shiftx") {
+ l_wire = shiftx->getPort("\\B");
+ q_wire = shiftx->getPort("\\Y");
+ shiftx->setPort("\\Y", cell->module->addWire(NEW_ID));
+ }
+ else if (shiftx->type == "$mux") {
+ l_wire = shiftx->getPort("\\S");
+ q_wire = shiftx->getPort("\\Y");
+ shiftx->setPort("\\Y", cell->module->addWire(NEW_ID));
+ }
+ else log_abort();
+
+ newcell->setPort("\\Q", q_wire);
+ newcell->setPort("\\L", l_wire);
+
+ return false;
+ }
+};
+
+
struct ShregmapWorker
{
Module *module;
@@ -113,8 +264,10 @@ struct ShregmapWorker
for (auto wire : module->wires())
{
if (wire->port_output || wire->get_bool_attribute("\\keep")) {
- for (auto bit : sigmap(wire))
+ for (auto bit : sigmap(wire)) {
sigbit_with_non_chain_users.insert(bit);
+ if (opts.tech) opts.tech->non_chain_user(bit, nullptr, {});
+ }
}
if (wire->attributes.count("\\init")) {
@@ -140,10 +293,22 @@ struct ShregmapWorker
if (opts.init || sigbit_init.count(q_bit) == 0)
{
- if (sigbit_chain_next.count(d_bit)) {
+ auto r = sigbit_chain_next.insert(std::make_pair(d_bit, cell));
+ if (!r.second) {
+ // Insertion not successful means that d_bit is already
+ // connected to another register, thus mark it as a
+ // non chain user ...
sigbit_with_non_chain_users.insert(d_bit);
- } else
- sigbit_chain_next[d_bit] = cell;
+ // ... and clone d_bit into another wire, and use that
+ // wire as a different key in the d_bit-to-cell dictionary
+ // so that it can be identified as another chain
+ // (omitting this common flop)
+ // Link: https://github.com/YosysHQ/yosys/pull/1085
+ Wire *wire = module->addWire(NEW_ID);
+ module->connect(wire, d_bit);
+ sigmap.add(wire, d_bit);
+ sigbit_chain_next.insert(std::make_pair(wire, cell));
+ }
sigbit_chain_prev[q_bit] = cell;
continue;
@@ -152,8 +317,10 @@ struct ShregmapWorker
for (auto conn : cell->connections())
if (cell->input(conn.first))
- for (auto bit : sigmap(conn.second))
+ for (auto bit : sigmap(conn.second)) {
sigbit_with_non_chain_users.insert(bit);
+ if (opts.tech) opts.tech->non_chain_user(bit, cell, conn.first);
+ }
}
}
@@ -258,7 +425,7 @@ struct ShregmapWorker
if (taps.empty() || taps.back() < depth-1)
taps.push_back(depth-1);
- if (opts.tech->analyze(taps))
+ if (opts.tech->analyze(taps, qbits))
break;
taps.pop_back();
@@ -377,6 +544,9 @@ struct ShregmapWorker
ShregmapWorker(Module *module, const ShregmapOptions &opts) :
module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
{
+ if (opts.tech)
+ opts.tech->init(module, sigmap);
+
make_sigbit_chain_next_prev();
find_chain_start_cells();
@@ -447,6 +617,11 @@ struct ShregmapPass : public Pass {
log("\n");
log(" -tech greenpak4\n");
log(" map to greenpak4 shift registers.\n");
+ log(" this option also implies -clkpol pos -zinit\n");
+ log("\n");
+ log(" -tech xilinx\n");
+ log(" map to xilinx dynamic-length shift registers.\n");
+ log(" this option also implies -params -init\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -501,6 +676,12 @@ struct ShregmapPass : public Pass {
clkpol = "pos";
opts.zinit = true;
opts.tech = new ShregmapTechGreenpak4;
+ }
+ else if (tech == "xilinx") {
+ opts.init = true;
+ opts.params = true;
+ enpol = "any_or_none";
+ opts.tech = new ShregmapTechXilinx7(opts);
} else {
argidx--;
break;
diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc
index 660b6060..f3da80c6 100644
--- a/passes/techmap/simplemap.cc
+++ b/passes/techmap/simplemap.cc
@@ -599,7 +599,7 @@ struct SimplemapPass : public Pass {
simplemap_get_mappers(mappers);
for (auto mod : design->modules()) {
- if (!design->selected(mod))
+ if (!design->selected(mod) || mod->get_blackbox_attribute())
continue;
std::vector<RTLIL::Cell*> cells = mod->cells();
for (auto cell : cells) {
diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc
index d0e5e223..ab0bd3b5 100644
--- a/passes/techmap/techmap.cc
+++ b/passes/techmap/techmap.cc
@@ -72,6 +72,8 @@ struct TechmapWorker
pool<IdString> flatten_done_list;
pool<Cell*> flatten_keep_list;
+ pool<string> log_msg_cache;
+
struct TechmapWireData {
RTLIL::Wire *wire;
RTLIL::SigSpec value;
@@ -84,6 +86,7 @@ struct TechmapWorker
bool flatten_mode;
bool recursive_mode;
bool autoproc_mode;
+ bool ignore_wb;
TechmapWorker()
{
@@ -92,6 +95,7 @@ struct TechmapWorker
flatten_mode = false;
recursive_mode = false;
autoproc_mode = false;
+ ignore_wb = false;
}
std::string constmap_tpl_name(SigMap &sigmap, RTLIL::Module *tpl, RTLIL::Cell *cell, bool verbose)
@@ -383,11 +387,12 @@ struct TechmapWorker
{
std::string mapmsg_prefix = in_recursion ? "Recursively mapping" : "Mapping";
- if (!design->selected(module))
+ if (!design->selected(module) || module->get_blackbox_attribute(ignore_wb))
return false;
bool log_continue = false;
bool did_something = false;
+ LogMakeDebugHdl mkdebug;
SigMap sigmap(module);
@@ -472,7 +477,7 @@ struct TechmapWorker
RTLIL::Module *tpl = map->modules_[tpl_name];
std::map<RTLIL::IdString, RTLIL::Const> parameters(cell->parameters.begin(), cell->parameters.end());
- if (tpl->get_bool_attribute("\\blackbox"))
+ if (tpl->get_blackbox_attribute(ignore_wb))
continue;
if (!flatten_mode)
@@ -545,6 +550,7 @@ struct TechmapWorker
if (extmapper_name == "wrap") {
std::string cmd_string = tpl->attributes.at("\\techmap_wrap").decode_string();
log("Running \"%s\" on wrapper %s.\n", cmd_string.c_str(), log_id(extmapper_module));
+ mkdebug.on();
Pass::call_on_module(extmapper_design, extmapper_module, cmd_string);
log_continue = true;
}
@@ -558,11 +564,21 @@ struct TechmapWorker
goto use_wrapper_tpl;
}
- log("%s %s.%s (%s) to %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(extmapper_module));
+ auto msg = stringf("Using extmapper %s for cells of type %s.", log_id(extmapper_module), log_id(cell->type));
+ if (!log_msg_cache.count(msg)) {
+ log_msg_cache.insert(msg);
+ log("%s\n", msg.c_str());
+ }
+ log_debug("%s %s.%s (%s) to %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(extmapper_module));
}
else
{
- log("%s %s.%s (%s) with %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), extmapper_name.c_str());
+ auto msg = stringf("Using extmapper %s for cells of type %s.", extmapper_name.c_str(), log_id(cell->type));
+ if (!log_msg_cache.count(msg)) {
+ log_msg_cache.insert(msg);
+ log("%s\n", msg.c_str());
+ }
+ log_debug("%s %s.%s (%s) with %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), extmapper_name.c_str());
if (extmapper_name == "simplemap") {
if (simplemap_mappers.count(cell->type) == 0)
@@ -660,6 +676,7 @@ struct TechmapWorker
tpl = techmap_cache[key];
} else {
if (parameters.size() != 0) {
+ mkdebug.on();
derived_name = tpl->derive(map, dict<RTLIL::IdString, RTLIL::Const>(parameters.begin(), parameters.end()));
tpl = map->module(derived_name);
log_continue = true;
@@ -829,6 +846,7 @@ struct TechmapWorker
if (log_continue) {
log_header(design, "Continuing TECHMAP pass.\n");
log_continue = false;
+ mkdebug.off();
}
while (techmap_module(map, tpl, map, handled_cells, celltypeMap, true)) { }
}
@@ -840,6 +858,7 @@ struct TechmapWorker
if (log_continue) {
log_header(design, "Continuing TECHMAP pass.\n");
log_continue = false;
+ mkdebug.off();
}
if (extern_mode && !in_recursion)
@@ -859,13 +878,18 @@ struct TechmapWorker
module_queue.insert(m);
}
- log("%s %s.%s to imported %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(m_name));
+ log_debug("%s %s.%s to imported %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(m_name));
cell->type = m_name;
cell->parameters.clear();
}
else
{
- log("%s %s.%s using %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(tpl));
+ auto msg = stringf("Using template %s for cells of type %s.", log_id(tpl), log_id(cell->type));
+ if (!log_msg_cache.count(msg)) {
+ log_msg_cache.insert(msg);
+ log("%s\n", msg.c_str());
+ }
+ log_debug("%s %s.%s (%s) using %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(tpl));
techmap_module_worker(design, module, cell, tpl);
cell = NULL;
}
@@ -883,6 +907,7 @@ struct TechmapWorker
if (log_continue) {
log_header(design, "Continuing TECHMAP pass.\n");
log_continue = false;
+ mkdebug.off();
}
return did_something;
@@ -925,6 +950,9 @@ struct TechmapPass : public Pass {
log(" -autoproc\n");
log(" Automatically call \"proc\" on implementations that contain processes.\n");
log("\n");
+ log(" -wb\n");
+ log(" Ignore the 'whitebox' attribute on cell implementations.\n");
+ log("\n");
log(" -assert\n");
log(" this option will cause techmap to exit with an error if it can't map\n");
log(" a selected cell. only cell types that end on an underscore are accepted\n");
@@ -1031,7 +1059,7 @@ struct TechmapPass : public Pass {
simplemap_get_mappers(worker.simplemap_mappers);
std::vector<std::string> map_files;
- std::string verilog_frontend = "verilog -nooverwrite";
+ std::string verilog_frontend = "verilog -nooverwrite -noblackbox";
int max_iter = -1;
size_t argidx;
@@ -1068,6 +1096,10 @@ struct TechmapPass : public Pass {
worker.autoproc_mode = true;
continue;
}
+ if (args[argidx] == "-wb") {
+ worker.ignore_wb = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -1076,7 +1108,7 @@ struct TechmapPass : public Pass {
if (map_files.empty()) {
std::istringstream f(stdcells_code);
Frontend::frontend_call(map, &f, "<techmap.v>", verilog_frontend);
- } else
+ } else {
for (auto &fn : map_files)
if (fn.substr(0, 1) == "%") {
if (!saved_designs.count(fn.substr(1))) {
@@ -1095,6 +1127,9 @@ struct TechmapPass : public Pass {
log_cmd_error("Can't open map file `%s'\n", fn.c_str());
Frontend::frontend_call(map, &f, fn, (fn.size() > 3 && fn.substr(fn.size()-3) == ".il") ? "ilang" : verilog_frontend);
}
+ }
+
+ log_header(design, "Continuing TECHMAP pass.\n");
std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap;
for (auto &it : map->modules_) {
@@ -1145,7 +1180,7 @@ struct FlattenPass : public Pass {
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" flatten [selection]\n");
+ log(" flatten [options] [selection]\n");
log("\n");
log("This pass flattens the design by replacing cells by their implementation. This\n");
log("pass is very similar to the 'techmap' pass. The only difference is that this\n");
@@ -1154,17 +1189,29 @@ struct FlattenPass : public Pass {
log("Cells and/or modules with the 'keep_hierarchy' attribute set will not be\n");
log("flattened by this command.\n");
log("\n");
+ log(" -wb\n");
+ log(" Ignore the 'whitebox' attribute on cell implementations.\n");
+ log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing FLATTEN pass (flatten design).\n");
log_push();
- extra_args(args, 1, design);
-
TechmapWorker worker;
worker.flatten_mode = true;
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-wb") {
+ worker.ignore_wb = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+
std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap;
for (auto module : design->modules())
celltypeMap[module->name].insert(module->name);
@@ -1190,6 +1237,7 @@ struct FlattenPass : public Pass {
}
}
+ log_suppressed();
log("No more expansions possible.\n");
if (top_mod != NULL)
@@ -1209,7 +1257,7 @@ struct FlattenPass : public Pass {
dict<RTLIL::IdString, RTLIL::Module*> new_modules;
for (auto mod : vector<Module*>(design->modules()))
- if (used_modules[mod->name] || mod->get_bool_attribute("\\blackbox")) {
+ if (used_modules[mod->name] || mod->get_blackbox_attribute(worker.ignore_wb)) {
new_modules[mod->name] = mod;
} else {
log("Deleting now unused module %s.\n", log_id(mod));
diff --git a/passes/techmap/zinit.cc b/passes/techmap/zinit.cc
index b46147fb..2aefc091 100644
--- a/passes/techmap/zinit.cc
+++ b/passes/techmap/zinit.cc
@@ -46,7 +46,7 @@ struct ZinitPass : public Pass {
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
- if (args[argidx] == "-singleton") {
+ if (args[argidx] == "-all") {
all_mode = true;
continue;
}
diff --git a/techlibs/anlogic/anlogic_determine_init.cc b/techlibs/anlogic/anlogic_determine_init.cc
index 34b1d4f8..c4089dac 100644
--- a/techlibs/anlogic/anlogic_determine_init.cc
+++ b/techlibs/anlogic/anlogic_determine_init.cc
@@ -50,7 +50,7 @@ struct AnlogicDetermineInitPass : public Pass {
extra_args(args, args.size(), design);
- size_t cnt = 0;
+ int cnt = 0;
for (auto module : design->selected_modules())
{
for (auto cell : module->selected_cells())
@@ -65,7 +65,7 @@ struct AnlogicDetermineInitPass : public Pass {
}
}
}
- log_header(design, "Updated %lu cells with determined init value.\n", cnt);
+ log_header(design, "Updated %d cells with determined init value.\n", cnt);
}
} AnlogicDetermineInitPass;
diff --git a/techlibs/anlogic/anlogic_eqn.cc b/techlibs/anlogic/anlogic_eqn.cc
index 741bf04c..070d39a2 100644
--- a/techlibs/anlogic/anlogic_eqn.cc
+++ b/techlibs/anlogic/anlogic_eqn.cc
@@ -69,7 +69,7 @@ struct AnlogicEqnPass : public Pass {
extra_args(args, args.size(), design);
- size_t cnt = 0;
+ int cnt = 0;
for (auto module : design->selected_modules())
{
for (auto cell : module->selected_cells())
@@ -106,7 +106,7 @@ struct AnlogicEqnPass : public Pass {
}
}
}
- log_header(design, "Updated %lu of AL_MAP_LUT* elements with equation.\n", cnt);
+ log_header(design, "Updated %d of AL_MAP_LUT* elements with equation.\n", cnt);
}
} AnlogicEqnPass;
diff --git a/techlibs/common/prep.cc b/techlibs/common/prep.cc
index 86fb4d6c..cdd21c3b 100644
--- a/techlibs/common/prep.cc
+++ b/techlibs/common/prep.cc
@@ -195,9 +195,11 @@ struct PrepPass : public ScriptPass
run(nokeepdc ? "opt" : "opt -keepdc");
if (!ifxmode) {
if (help_mode)
- run("wreduce [-memx]");
- else
+ run("wreduce -keepdc [-memx]");
+ else if (nokeepdc)
run(memxmode ? "wreduce -memx" : "wreduce");
+ else
+ run(memxmode ? "wreduce -keepdc -memx" : "wreduce -keepdc");
}
if (!nomemmode) {
run(string("memory_dff") + (help_mode ? " [-nordff]" : nordff ? " -nordff" : ""));
diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v
index 8e43fe05..a424d308 100644
--- a/techlibs/common/simlib.v
+++ b/techlibs/common/simlib.v
@@ -1271,6 +1271,181 @@ endmodule
// --------------------------------------------------------
+module \$specify2 (EN, SRC, DST);
+
+parameter FULL = 0;
+parameter SRC_WIDTH = 1;
+parameter DST_WIDTH = 1;
+
+parameter SRC_DST_PEN = 0;
+parameter SRC_DST_POL = 0;
+
+parameter T_RISE_MIN = 0;
+parameter T_RISE_TYP = 0;
+parameter T_RISE_MAX = 0;
+
+parameter T_FALL_MIN = 0;
+parameter T_FALL_TYP = 0;
+parameter T_FALL_MAX = 0;
+
+input EN;
+input [SRC_WIDTH-1:0] SRC;
+input [DST_WIDTH-1:0] DST;
+
+localparam SD = SRC_DST_PEN ? (SRC_DST_POL ? 1 : 2) : 0;
+
+`ifdef SIMLIB_SPECIFY
+specify
+ if (EN && SD==0 && !FULL) (SRC => DST) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && SD==0 && FULL) (SRC *> DST) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && SD==1 && !FULL) (SRC +=> DST) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && SD==1 && FULL) (SRC +*> DST) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && SD==2 && !FULL) (SRC -=> DST) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && SD==2 && FULL) (SRC -*> DST) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+endspecify
+`endif
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$specify3 (EN, SRC, DST, DAT);
+
+parameter FULL = 0;
+parameter SRC_WIDTH = 1;
+parameter DST_WIDTH = 1;
+
+parameter EDGE_EN = 0;
+parameter EDGE_POL = 0;
+
+parameter SRC_DST_PEN = 0;
+parameter SRC_DST_POL = 0;
+
+parameter DAT_DST_PEN = 0;
+parameter DAT_DST_POL = 0;
+
+parameter T_RISE_MIN = 0;
+parameter T_RISE_TYP = 0;
+parameter T_RISE_MAX = 0;
+
+parameter T_FALL_MIN = 0;
+parameter T_FALL_TYP = 0;
+parameter T_FALL_MAX = 0;
+
+input EN;
+input [SRC_WIDTH-1:0] SRC;
+input [DST_WIDTH-1:0] DST, DAT;
+
+localparam ED = EDGE_EN ? (EDGE_POL ? 1 : 2) : 0;
+localparam SD = SRC_DST_PEN ? (SRC_DST_POL ? 1 : 2) : 0;
+localparam DD = DAT_DST_PEN ? (DAT_DST_POL ? 1 : 2) : 0;
+
+`ifdef SIMLIB_SPECIFY
+specify
+ // DD=0
+
+ if (EN && DD==0 && SD==0 && ED==0 && !FULL) ( SRC => (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==0 && SD==0 && ED==0 && FULL) ( SRC *> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==0 && SD==0 && ED==1 && !FULL) (posedge SRC => (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==0 && SD==0 && ED==1 && FULL) (posedge SRC *> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==0 && SD==0 && ED==2 && !FULL) (negedge SRC => (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==0 && SD==0 && ED==2 && FULL) (negedge SRC *> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+
+ if (EN && DD==0 && SD==1 && ED==0 && !FULL) ( SRC +=> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==0 && SD==1 && ED==0 && FULL) ( SRC +*> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==0 && SD==1 && ED==1 && !FULL) (posedge SRC +=> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==0 && SD==1 && ED==1 && FULL) (posedge SRC +*> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==0 && SD==1 && ED==2 && !FULL) (negedge SRC +=> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==0 && SD==1 && ED==2 && FULL) (negedge SRC +*> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+
+ if (EN && DD==0 && SD==2 && ED==0 && !FULL) ( SRC -=> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==0 && SD==2 && ED==0 && FULL) ( SRC -*> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==0 && SD==2 && ED==1 && !FULL) (posedge SRC -=> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==0 && SD==2 && ED==1 && FULL) (posedge SRC -*> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==0 && SD==2 && ED==2 && !FULL) (negedge SRC -=> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==0 && SD==2 && ED==2 && FULL) (negedge SRC -*> (DST : DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+
+ // DD=1
+
+ if (EN && DD==1 && SD==0 && ED==0 && !FULL) ( SRC => (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==1 && SD==0 && ED==0 && FULL) ( SRC *> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==1 && SD==0 && ED==1 && !FULL) (posedge SRC => (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==1 && SD==0 && ED==1 && FULL) (posedge SRC *> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==1 && SD==0 && ED==2 && !FULL) (negedge SRC => (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==1 && SD==0 && ED==2 && FULL) (negedge SRC *> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+
+ if (EN && DD==1 && SD==1 && ED==0 && !FULL) ( SRC +=> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==1 && SD==1 && ED==0 && FULL) ( SRC +*> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==1 && SD==1 && ED==1 && !FULL) (posedge SRC +=> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==1 && SD==1 && ED==1 && FULL) (posedge SRC +*> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==1 && SD==1 && ED==2 && !FULL) (negedge SRC +=> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==1 && SD==1 && ED==2 && FULL) (negedge SRC +*> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+
+ if (EN && DD==1 && SD==2 && ED==0 && !FULL) ( SRC -=> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==1 && SD==2 && ED==0 && FULL) ( SRC -*> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==1 && SD==2 && ED==1 && !FULL) (posedge SRC -=> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==1 && SD==2 && ED==1 && FULL) (posedge SRC -*> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==1 && SD==2 && ED==2 && !FULL) (negedge SRC -=> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==1 && SD==2 && ED==2 && FULL) (negedge SRC -*> (DST +: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+
+ // DD=2
+
+ if (EN && DD==2 && SD==0 && ED==0 && !FULL) ( SRC => (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==2 && SD==0 && ED==0 && FULL) ( SRC *> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==2 && SD==0 && ED==1 && !FULL) (posedge SRC => (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==2 && SD==0 && ED==1 && FULL) (posedge SRC *> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==2 && SD==0 && ED==2 && !FULL) (negedge SRC => (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==2 && SD==0 && ED==2 && FULL) (negedge SRC *> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+
+ if (EN && DD==2 && SD==1 && ED==0 && !FULL) ( SRC +=> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==2 && SD==1 && ED==0 && FULL) ( SRC +*> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==2 && SD==1 && ED==1 && !FULL) (posedge SRC +=> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==2 && SD==1 && ED==1 && FULL) (posedge SRC +*> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==2 && SD==1 && ED==2 && !FULL) (negedge SRC +=> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==2 && SD==1 && ED==2 && FULL) (negedge SRC +*> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+
+ if (EN && DD==2 && SD==2 && ED==0 && !FULL) ( SRC -=> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==2 && SD==2 && ED==0 && FULL) ( SRC -*> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==2 && SD==2 && ED==1 && !FULL) (posedge SRC -=> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==2 && SD==2 && ED==1 && FULL) (posedge SRC -*> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==2 && SD==2 && ED==2 && !FULL) (negedge SRC -=> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+ if (EN && DD==2 && SD==2 && ED==2 && FULL) (negedge SRC -*> (DST -: DAT)) = (T_RISE_MIN:T_RISE_TYP:T_RISE_MAX, T_FALL_MIN:T_FALL_TYP:T_FALL_MAX);
+endspecify
+`endif
+
+endmodule
+
+// --------------------------------------------------------
+
+module \$specrule (EN_SRC, EN_DST, SRC, DST);
+
+parameter TYPE = "";
+parameter T_LIMIT = 0;
+parameter T_LIMIT2 = 0;
+
+parameter SRC_WIDTH = 1;
+parameter DST_WIDTH = 1;
+
+parameter SRC_PEN = 0;
+parameter SRC_POL = 0;
+
+parameter DST_PEN = 0;
+parameter DST_POL = 0;
+
+input EN_SRC, EN_DST;
+input [SRC_WIDTH-1:0] SRC;
+input [DST_WIDTH-1:0] DST;
+
+`ifdef SIMLIB_SPECIFY
+specify
+ // TBD
+endspecify
+`endif
+
+endmodule
+
+// --------------------------------------------------------
+
module \$assert (A, EN);
input A, EN;
@@ -1863,4 +2038,5 @@ end
endmodule
`endif
+
// --------------------------------------------------------
diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc
index ccfa76e0..e41c0fe9 100644
--- a/techlibs/common/synth.cc
+++ b/techlibs/common/synth.cc
@@ -201,6 +201,8 @@ struct SynthPass : public ScriptPass
run("check");
run("opt");
run("wreduce");
+ run("peepopt");
+ run("opt_clean");
if (help_mode)
run("techmap -map +/cmp2lut.v", " (if -lut)");
else
diff --git a/techlibs/ecp5/arith_map.v b/techlibs/ecp5/arith_map.v
index eb794760..17bde049 100644
--- a/techlibs/ecp5/arith_map.v
+++ b/techlibs/ecp5/arith_map.v
@@ -50,20 +50,21 @@ module _80_ecp5_alu (A, B, CI, BI, X, Y, CO);
wire [Y_WIDTH2-1:0] AA = A_buf;
wire [Y_WIDTH2-1:0] BB = BI ? ~B_buf : B_buf;
+ wire [Y_WIDTH2-1:0] BX = B_buf;
wire [Y_WIDTH2-1:0] C = {CO, CI};
wire [Y_WIDTH2-1:0] FCO, Y1;
genvar i;
generate for (i = 0; i < Y_WIDTH2; i = i + 2) begin:slice
CCU2C #(
- .INIT0(16'b0110011010101010),
- .INIT1(16'b0110011010101010),
+ .INIT0(16'b1001011010101010),
+ .INIT1(16'b1001011010101010),
.INJECT1_0("NO"),
.INJECT1_1("NO")
) ccu2c_i (
.CIN(C[i]),
- .A0(AA[i]), .B0(BB[i]), .C0(1'b0), .D0(1'b1),
- .A1(AA[i+1]), .B1(BB[i+1]), .C1(1'b0), .D1(1'b1),
+ .A0(AA[i]), .B0(BX[i]), .C0(BI), .D0(1'b1),
+ .A1(AA[i+1]), .B1(BX[i+1]), .C1(BI), .D1(1'b1),
.S0(Y[i]), .S1(Y1[i]),
.COUT(FCO[i])
);
diff --git a/techlibs/ecp5/cells_map.v b/techlibs/ecp5/cells_map.v
index 6ab4b69f..a90d5e03 100644
--- a/techlibs/ecp5/cells_map.v
+++ b/techlibs/ecp5/cells_map.v
@@ -47,8 +47,59 @@ module \$__DFFSE_NP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"
module \$__DFFSE_PP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule
module \$__DFFSE_PP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule
-// For Diamond compatibility, FIXME: add all Diamond flipflop mappings
+// TODO: Diamond flip-flops
+// module FD1P3AX(); endmodule
+// module FD1P3AY(); endmodule
+// module FD1P3BX(); endmodule
+// module FD1P3DX(); endmodule
+// module FD1P3IX(); endmodule
+// module FD1P3JX(); endmodule
+// module FD1S3AX(); endmodule
+// module FD1S3AY(); endmodule
module FD1S3BX(input PD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule
+module FD1S3DX(input CD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule
+module FD1S3IX(input CD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule
+module FD1S3JX(input PD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule
+// module FL1P3AY(); endmodule
+// module FL1P3AZ(); endmodule
+// module FL1P3BX(); endmodule
+// module FL1P3DX(); endmodule
+// module FL1P3IY(); endmodule
+// module FL1P3JY(); endmodule
+// module FL1S3AX(); endmodule
+// module FL1S3AY(); endmodule
+
+// Diamond I/O buffers
+module IB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(I), .O(O)); endmodule
+module IBPU (input I, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(I), .O(O)); endmodule
+module IBPD (input I, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(I), .O(O)); endmodule
+module OB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I)); endmodule
+module OBZ (input I, T, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I), .T(T)); endmodule
+module OBZPU(input I, T, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I), .T(T)); endmodule
+module OBZPD(input I, T, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I), .T(T)); endmodule
+module OBCO (input I, output OT, OC); OLVDS _TECHMAP_REPLACE_ (.A(I), .Z(OT), .ZN(OC)); endmodule
+module BB (input I, T, output O, inout B); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.B(B), .I(I), .O(O), .T(T)); endmodule
+module BBPU (input I, T, output O, inout B); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.B(B), .I(I), .O(O), .T(T)); endmodule
+module BBPD (input I, T, output O, inout B); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.B(B), .I(I), .O(O), .T(T)); endmodule
+module ILVDS(input A, AN, output Z); TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(A), .O(Z)); endmodule
+module OLVDS(input A, output Z, ZN); TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(Z), .I(A)); endmodule
+
+// Diamond I/O registers
+module IFS1P3BX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module IFS1P3DX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module IFS1P3IX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module IFS1P3JX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+
+module OFS1P3BX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module OFS1P3DX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module OFS1P3IX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module OFS1P3JX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+
+// TODO: Diamond I/O latches
+// module IFS1S1B(input PD, D, SCLK, output Q); endmodule
+// module IFS1S1D(input CD, D, SCLK, output Q); endmodule
+// module IFS1S1I(input PD, D, SCLK, output Q); endmodule
+// module IFS1S1J(input CD, D, SCLK, output Q); endmodule
`ifndef NO_LUT
module \$lut (A, Y);
diff --git a/techlibs/ecp5/cells_sim.v b/techlibs/ecp5/cells_sim.v
index 1e4002ee..07fadfa1 100644
--- a/techlibs/ecp5/cells_sim.v
+++ b/techlibs/ecp5/cells_sim.v
@@ -251,18 +251,6 @@ module TRELLIS_FF(input CLK, LSR, CE, DI, M, output reg Q);
endmodule
// ---------------------------------------
-
-module OBZ(input I, T, output O);
-assign O = T ? 1'bz : I;
-endmodule
-
-// ---------------------------------------
-
-module IB(input I, output O);
-assign O = I;
-endmodule
-
-// ---------------------------------------
(* keep *)
module TRELLIS_IO(
inout B,
@@ -293,19 +281,6 @@ endmodule
// ---------------------------------------
-module OB(input I, output O);
-assign O = I;
-endmodule
-
-// ---------------------------------------
-
-module BB(input I, T, output O, inout B);
-assign B = T ? 1'bz : I;
-assign O = B;
-endmodule
-
-// ---------------------------------------
-
module INV(input A, output Z);
assign Z = !A;
endmodule
@@ -558,19 +533,56 @@ module DP16KD(
parameter INITVAL_3F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
endmodule
-// For Diamond compatibility, FIXME: add all Diamond flipflop mappings
-module FD1S3BX(input PD, D, CK, output Q);
- TRELLIS_FF #(
- .GSR("DISABLED"),
- .CEMUX("1"),
- .CLKMUX("CLK"),
- .LSRMUX("LSR"),
- .REGSET("SET"),
- .SRMODE("ASYNC")
- ) tff_i (
- .CLK(CK),
- .LSR(PD),
- .DI(D),
- .Q(Q)
- );
-endmodule
+// TODO: Diamond flip-flops
+// module FD1P3AX(); endmodule
+// module FD1P3AY(); endmodule
+// module FD1P3BX(); endmodule
+// module FD1P3DX(); endmodule
+// module FD1P3IX(); endmodule
+// module FD1P3JX(); endmodule
+// module FD1S3AX(); endmodule
+// module FD1S3AY(); endmodule
+module FD1S3BX(input PD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) tff (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule
+module FD1S3DX(input CD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) tff (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule
+module FD1S3IX(input CD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule
+module FD1S3JX(input PD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule
+// module FL1P3AY(); endmodule
+// module FL1P3AZ(); endmodule
+// module FL1P3BX(); endmodule
+// module FL1P3DX(); endmodule
+// module FL1P3IY(); endmodule
+// module FL1P3JY(); endmodule
+// module FL1S3AX(); endmodule
+// module FL1S3AY(); endmodule
+
+// Diamond I/O buffers
+module IB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("INPUT")) tio (.B(I), .O(O)); endmodule
+module IBPU (input I, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("INPUT")) tio (.B(I), .O(O)); endmodule
+module IBPD (input I, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("INPUT")) tio (.B(I), .O(O)); endmodule
+module OB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) tio (.B(O), .I(I)); endmodule
+module OBZ (input I, T, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) tio (.B(O), .I(I), .T(T)); endmodule
+module OBZPU(input I, T, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("OUTPUT")) tio (.B(O), .I(I), .T(T)); endmodule
+module OBZPD(input I, T, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("OUTPUT")) tio (.B(O), .I(I), .T(T)); endmodule
+module OBCO (input I, output OT, OC); OLVDS olvds (.A(I), .Z(OT), .ZN(OC)); endmodule
+module BB (input I, T, output O, inout B); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("BIDIR")) tio (.B(B), .I(I), .O(O), .T(T)); endmodule
+module BBPU (input I, T, output O, inout B); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("BIDIR")) tio (.B(B), .I(I), .O(O), .T(T)); endmodule
+module BBPD (input I, T, output O, inout B); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("BIDIR")) tio (.B(B), .I(I), .O(O), .T(T)); endmodule
+module ILVDS(input A, AN, output Z); TRELLIS_IO #(.DIR("INPUT")) tio (.B(A), .O(Z)); endmodule
+module OLVDS(input A, output Z, ZN); TRELLIS_IO #(.DIR("OUTPUT")) tio (.B(Z), .I(A)); endmodule
+
+// Diamond I/O registers
+module IFS1P3BX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) tff (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module IFS1P3DX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) tff (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module IFS1P3IX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module IFS1P3JX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+
+module OFS1P3BX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) tff (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module OFS1P3DX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) tff (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module OFS1P3IX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module OFS1P3JX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+
+// TODO: Diamond I/O latches
+// module IFS1S1B(input PD, D, SCLK, output Q); endmodule
+// module IFS1S1D(input CD, D, SCLK, output Q); endmodule
+// module IFS1S1I(input PD, D, SCLK, output Q); endmodule
+// module IFS1S1J(input CD, D, SCLK, output Q); endmodule
diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc
index 4b889d67..01222e55 100644
--- a/techlibs/ecp5/synth_ecp5.cc
+++ b/techlibs/ecp5/synth_ecp5.cc
@@ -76,7 +76,7 @@ struct SynthEcp5Pass : public ScriptPass
log(" -nodram\n");
log(" do not use distributed RAM cells in output netlist\n");
log("\n");
- log(" -nomux\n");
+ log(" -nowidelut\n");
log(" do not use PFU muxes to implement LUTs larger than LUT4s\n");
log("\n");
log(" -abc2\n");
@@ -93,7 +93,7 @@ struct SynthEcp5Pass : public ScriptPass
}
string top_opt, blif_file, edif_file, json_file;
- bool noccu2, nodffe, nobram, nodram, nomux, flatten, retime, abc2, vpr;
+ bool noccu2, nodffe, nobram, nodram, nowidelut, flatten, retime, abc2, vpr;
void clear_flags() YS_OVERRIDE
{
@@ -105,7 +105,7 @@ struct SynthEcp5Pass : public ScriptPass
nodffe = false;
nobram = false;
nodram = false;
- nomux = false;
+ nowidelut = false;
flatten = true;
retime = false;
abc2 = false;
@@ -172,8 +172,8 @@ struct SynthEcp5Pass : public ScriptPass
nodram = true;
continue;
}
- if (args[argidx] == "-nomux") {
- nomux = true;
+ if (args[argidx] == "-nowidelut" || args[argidx] == "-nomux") {
+ nowidelut = true;
continue;
}
if (args[argidx] == "-abc2") {
@@ -253,7 +253,7 @@ struct SynthEcp5Pass : public ScriptPass
if (!nodffe)
run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*");
run("techmap -D NO_LUT -map +/ecp5/cells_map.v");
- run("opt_expr -mux_undef");
+ run("opt_expr -undriven -mux_undef");
run("simplemap");
run("ecp5_ffinit");
}
@@ -264,7 +264,7 @@ struct SynthEcp5Pass : public ScriptPass
run("abc", " (only if -abc2)");
}
run("techmap -map +/ecp5/latches_map.v");
- if (nomux)
+ if (nowidelut)
run("abc -lut 4 -dress");
else
run("abc -lut 4:7 -dress");
diff --git a/techlibs/gowin/Makefile.inc b/techlibs/gowin/Makefile.inc
index 2f82def7..6f215934 100644
--- a/techlibs/gowin/Makefile.inc
+++ b/techlibs/gowin/Makefile.inc
@@ -1,7 +1,17 @@
OBJS += techlibs/gowin/synth_gowin.o
+OBJS += techlibs/gowin/determine_init.o
+
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_map.v))
$(eval $(call add_share_file,share/gowin,techlibs/gowin/cells_sim.v))
$(eval $(call add_share_file,share/gowin,techlibs/gowin/arith_map.v))
+$(eval $(call add_share_file,share/gowin,techlibs/gowin/brams_map.v))
+$(eval $(call add_share_file,share/gowin,techlibs/gowin/bram.txt))
+$(eval $(call add_share_file,share/gowin,techlibs/gowin/drams_map.v))
+$(eval $(call add_share_file,share/gowin,techlibs/gowin/dram.txt))
+
+
+
+$(eval $(call add_share_file,share/gowin,techlibs/gowin/brams_init3.vh))
diff --git a/techlibs/gowin/bram.txt b/techlibs/gowin/bram.txt
new file mode 100644
index 00000000..b5f9a981
--- /dev/null
+++ b/techlibs/gowin/bram.txt
@@ -0,0 +1,29 @@
+bram $__GW1NR_SDP
+# uncomment when done
+# init 1
+ abits 10 @a10d18
+ dbits 16 @a10d18
+ abits 11 @a11d9
+ dbits 8 @a11d9
+ abits 12 @a12d4
+ dbits 4 @a12d4
+ abits 13 @a13d2
+ dbits 2 @a13d2
+ abits 14 @a14d1
+ dbits 1 @a14d1
+ groups 2
+ ports 1 1
+ wrmode 1 0
+ enable 1 1 @a10d18
+ enable 1 1 @a11d9 @a12d4 @a13d2 @a14d1
+ transp 0 0
+ clocks 2 3
+ clkpol 2 3
+endbram
+
+match $__GW1NR_SDP
+ min bits 2048
+ min efficiency 5
+ shuffle_enable B
+ make_transp
+endmatch
diff --git a/techlibs/gowin/brams_init3.vh b/techlibs/gowin/brams_init3.vh
new file mode 100644
index 00000000..84397fa2
--- /dev/null
+++ b/techlibs/gowin/brams_init3.vh
@@ -0,0 +1,12 @@
+localparam [15:0] INIT_0 = {
+ INIT[ 60], INIT[ 56], INIT[ 52], INIT[ 48], INIT[ 44], INIT[ 40], INIT[ 36], INIT[ 32], INIT[ 28], INIT[ 24], INIT[ 20], INIT[ 16], INIT[ 12], INIT[ 8], INIT[ 4], INIT[ 0]
+};
+localparam [15:0] INIT_1 = {
+ INIT[ 61], INIT[ 57], INIT[ 53], INIT[ 49], INIT[ 45], INIT[ 41], INIT[ 37], INIT[ 33], INIT[ 29], INIT[ 25], INIT[ 21], INIT[ 17], INIT[ 13], INIT[ 9], INIT[ 5], INIT[ 1]
+};
+localparam [15:0] INIT_2 = {
+ INIT[ 62], INIT[ 58], INIT[ 54], INIT[ 50], INIT[ 46], INIT[ 42], INIT[ 38], INIT[ 34], INIT[ 30], INIT[ 26], INIT[ 22], INIT[ 18], INIT[ 14], INIT[ 10], INIT[ 6], INIT[ 2]
+};
+localparam [15:0] INIT_3 = {
+ INIT[ 63], INIT[ 59], INIT[ 55], INIT[ 51], INIT[ 47], INIT[ 43], INIT[ 39], INIT[ 35], INIT[ 31], INIT[ 27], INIT[ 23], INIT[ 19], INIT[ 15], INIT[ 11], INIT[ 7], INIT[ 3]
+};
diff --git a/techlibs/gowin/brams_map.v b/techlibs/gowin/brams_map.v
new file mode 100644
index 00000000..e963cfa8
--- /dev/null
+++ b/techlibs/gowin/brams_map.v
@@ -0,0 +1,103 @@
+/* Semi Dual Port (SDP) memory have the following configurations:
+ * Memory Config RAM(BIT) Port Mode Memory Depth Data Depth
+ * ----------------|---------| ----------|--------------|------------|
+ * B-SRAM_16K_SD1 16K 16Kx1 16,384 1
+ * B-SRAM_8K_SD2 16K 8Kx2 8,192 2
+ * B-SRAM_4K_SD4 16K 4Kx2 4,096 4
+ */
+module \$__GW1NR_SDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
+ parameter CFG_ABITS = 10;
+ parameter CFG_DBITS = 16;
+ parameter CFG_ENABLE_A = 3;
+
+ parameter [16383:0] INIT = 16384'hx;
+ parameter CLKPOL2 = 1;
+ parameter CLKPOL3 = 1;
+
+ input CLK2;
+ input CLK3;
+
+ input [CFG_ABITS-1:0] A1ADDR;
+ input [CFG_DBITS-1:0] A1DATA;
+ input [CFG_ENABLE_A-1:0] A1EN;
+
+ input [CFG_ABITS-1:0] B1ADDR;
+ output [CFG_DBITS-1:0] B1DATA;
+ input B1EN;
+
+
+ generate if (CFG_DBITS == 1) begin
+ SDP #(
+ .READ_MODE(0),
+ .BIT_WIDTH_0(1),
+ .BIT_WIDTH_1(1),
+ .BLK_SEL(3'b000),
+ .RESET_MODE("SYNC")
+ ) _TECHMAP_REPLACE_ (
+ .CLKA(CLK2), .CLKB(CLK3),
+ .WREA(A1EN), .OCE(1'b0), .CEA(1'b1),
+ .WREB(1'b0), .CEB(B1EN),
+ .RESETA(1'b0), .RESETB(1'b0), .BLKSEL(3'b000),
+ .DI(A1DATA), .DO(B1DATA), .ADA(A1ADDR), .ADB(B1ADDR)
+ );
+ end else if (CFG_DBITS == 2) begin
+ SDP #(
+ .READ_MODE(0),
+ .BIT_WIDTH_0(2),
+ .BIT_WIDTH_1(2),
+ .BLK_SEL(3'b000),
+ .RESET_MODE("SYNC")
+ ) _TECHMAP_REPLACE_ (
+ .CLKA(CLK2), .CLKB(CLK3),
+ .WREA(A1EN), .OCE(1'b0), .CEA(1'b1),
+ .WREB(1'b0), .CEB(B1EN),
+ .RESETA(1'b0), .RESETB(1'b0), .BLKSEL(3'b000),
+ .DI(A1DATA), .DO(B1DATA), .ADA(A1ADDR), .ADB(B1ADDR)
+ );
+ end else if (CFG_DBITS <= 4) begin
+ SDP #(
+ .READ_MODE(0),
+ .BIT_WIDTH_0(4),
+ .BIT_WIDTH_1(4),
+ .BLK_SEL(3'b000),
+ .RESET_MODE("SYNC")
+ ) _TECHMAP_REPLACE_ (
+ .CLKA(CLK2), .CLKB(CLK3),
+ .WREA(A1EN), .OCE(1'b0),
+ .WREB(1'b0), .CEB(B1EN), .CEA(1'b1),
+ .RESETA(1'b0), .RESETB(1'b0), .BLKSEL(3'b000),
+ .DI(A1DATA), .DO(B1DATA), .ADA(A1ADDR), .ADB(B1ADDR)
+ );
+ end else if (CFG_DBITS <= 8) begin
+ SDP #(
+ .READ_MODE(0),
+ .BIT_WIDTH_0(8),
+ .BIT_WIDTH_1(8),
+ .BLK_SEL(3'b000),
+ .RESET_MODE("SYNC")
+ ) _TECHMAP_REPLACE_ (
+ .CLKA(CLK2), .CLKB(CLK3),
+ .WREA(A1EN), .OCE(1'b0), .CEA(1'b1),
+ .WREB(1'b0), .CEB(B1EN),
+ .RESETA(1'b0), .RESETB(1'b0), .BLKSEL(3'b000),
+ .DI(A1DATA), .DO(B1DATA), .ADA(A1ADDR), .ADB(B1ADDR)
+ );
+ end else if (CFG_DBITS <= 16) begin
+ SDP #(
+ .READ_MODE(0),
+ .BIT_WIDTH_0(16),
+ .BIT_WIDTH_1(16),
+ .BLK_SEL(3'b000),
+ .RESET_MODE("SYNC")
+ ) _TECHMAP_REPLACE_ (
+ .CLKA(CLK2), .CLKB(CLK3),
+ .WREA(A1EN), .OCE(1'b0),
+ .WREB(1'b0), .CEB(B1EN), .CEA(1'b1),
+ .RESETA(1'b0), .RESETB(1'b0), .BLKSEL(3'b000),
+ .DI(A1DATA), .DO(B1DATA), .ADA(A1ADDR), .ADB(B1ADDR)
+ );
+ end else begin
+ wire TECHMAP_FAIL = 1'b1;
+ end endgenerate
+
+endmodule
diff --git a/techlibs/gowin/cells_map.v b/techlibs/gowin/cells_map.v
index e1f85eff..ebdc88a0 100644
--- a/techlibs/gowin/cells_map.v
+++ b/techlibs/gowin/cells_map.v
@@ -1,5 +1,9 @@
module \$_DFF_N_ (input D, C, output Q); DFFN _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C)); endmodule
-module \$_DFF_P_ (input D, C, output Q); DFF _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C)); endmodule
+module \$_DFF_P_ #(parameter INIT = 1'b0) (input D, C, output Q); DFF #(.INIT(INIT)) _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C)); endmodule
+
+module \$__DFFS_PN0_ (input D, C, R, output Q); DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(!R)); endmodule
+module \$__DFFS_PP0_ (input D, C, R, output Q); DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R)); endmodule
+module \$__DFFS_PP1_ (input D, C, R, output Q); DFFR _TECHMAP_REPLACE_ (.D(D), .Q(Q), .CLK(C), .RESET(R)); endmodule
module \$lut (A, Y);
parameter WIDTH = 0;
diff --git a/techlibs/gowin/cells_sim.v b/techlibs/gowin/cells_sim.v
index 14441c2f..ebb238ba 100644
--- a/techlibs/gowin/cells_sim.v
+++ b/techlibs/gowin/cells_sim.v
@@ -38,6 +38,17 @@ module DFFN (output reg Q, input CLK, D);
Q <= D;
endmodule
+module DFFR (output reg Q, input D, CLK, RESET);
+ parameter [0:0] INIT = 1'b0;
+ initial Q = INIT;
+ always @(posedge CLK) begin
+ if (RESET)
+ Q <= 1'b0;
+ else
+ Q <= D;
+ end
+endmodule // DFFR (positive clock edge; synchronous reset)
+
module VCC(output V);
assign V = 1;
endmodule
@@ -63,3 +74,126 @@ module ALU (input I0, input I1, input I3, input CIN, output COUT, output SUM);
assign {COUT, SUM} = CIN + I1 + I0;
endmodule // alu
+module RAM16S4 (DO, DI, AD, WRE, CLK);
+ parameter WIDTH = 4;
+ parameter INIT_0 = 16'h0000;
+ parameter INIT_1 = 16'h0000;
+ parameter INIT_2 = 16'h0000;
+ parameter INIT_3 = 16'h0000;
+
+ input [WIDTH-1:0] AD;
+ input [WIDTH-1:0] DI;
+ output [WIDTH-1:0] DO;
+ input CLK;
+ input WRE;
+
+ reg [15:0] mem0, mem1, mem2, mem3;
+
+ initial begin
+ mem0 = INIT_0;
+ mem1 = INIT_1;
+ mem2 = INIT_2;
+ mem3 = INIT_3;
+ end
+
+ assign DO[0] = mem0[AD];
+ assign DO[1] = mem1[AD];
+ assign DO[2] = mem2[AD];
+ assign DO[3] = mem3[AD];
+
+ always @(posedge CLK) begin
+ if (WRE) begin
+ mem0[AD] <= DI[0];
+ mem1[AD] <= DI[1];
+ mem2[AD] <= DI[2];
+ mem3[AD] <= DI[3];
+ end
+ end
+
+endmodule // RAM16S4
+
+
+(* blackbox *)
+module SDP (DO, DI, BLKSEL, ADA, ADB, WREA, WREB, CLKA, CLKB, CEA, CEB, OCE, RESETA, RESETB);
+//1'b0: Bypass mode; 1'b1 Pipeline mode
+parameter READ_MODE = 1'b0;
+parameter BIT_WIDTH_0 = 32; // 1, 2, 4, 8, 16, 32
+parameter BIT_WIDTH_1 = 32; // 1, 2, 4, 8, 16, 32
+parameter BLK_SEL = 3'b000;
+parameter RESET_MODE = "SYNC";
+parameter INIT_RAM_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_02 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_03 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_04 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_05 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_06 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_07 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_08 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_09 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_0A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_0B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_0C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_0D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_0E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_0F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_10 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_12 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_13 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_14 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_15 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_16 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_17 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_18 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_19 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_1A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_1B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_1C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_1D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_1E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_1F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_20 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_21 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_22 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_23 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_24 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_25 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_26 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_27 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_28 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_29 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_2A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_2B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_2C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_2D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_2E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_2F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_30 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_31 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_32 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_33 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_34 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_35 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_36 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_37 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_38 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_39 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_3A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_3B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_3C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_3D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_3E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+parameter INIT_RAM_3F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+
+input CLKA, CEA, CLKB, CEB;
+input OCE; // clock enable of memory output register
+input RESETA, RESETB; // resets output registers, not memory contents
+input WREA, WREB; // 1'b0: read enabled; 1'b1: write enabled
+input [13:0] ADA, ADB;
+input [31:0] DI;
+input [2:0] BLKSEL;
+output [31:0] DO;
+
+endmodule
+
diff --git a/techlibs/gowin/determine_init.cc b/techlibs/gowin/determine_init.cc
new file mode 100644
index 00000000..d9a0880f
--- /dev/null
+++ b/techlibs/gowin/determine_init.cc
@@ -0,0 +1,72 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2018 Icenowy Zheng <icenowy@aosc.io>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct DetermineInitPass : public Pass {
+ DetermineInitPass() : Pass("determine_init", "Determine the init value of cells") { }
+ void help() YS_OVERRIDE
+ {
+ log("\n");
+ log(" determine_init [selection]\n");
+ log("\n");
+ log("Determine the init value of cells that doesn't allow unknown init value.\n");
+ log("\n");
+ }
+
+ Const determine_init(Const init)
+ {
+ for (int i = 0; i < GetSize(init); i++) {
+ if (init[i] != State::S0 && init[i] != State::S1)
+ init[i] = State::S0;
+ }
+
+ return init;
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing DETERMINE_INIT pass (determine init value for cells).\n");
+
+ extra_args(args, args.size(), design);
+
+ int cnt = 0;
+ for (auto module : design->selected_modules())
+ {
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type == "\\RAM16S4")
+ {
+ cell->setParam("\\INIT_0", determine_init(cell->getParam("\\INIT_0")));
+ cell->setParam("\\INIT_1", determine_init(cell->getParam("\\INIT_1")));
+ cell->setParam("\\INIT_2", determine_init(cell->getParam("\\INIT_2")));
+ cell->setParam("\\INIT_3", determine_init(cell->getParam("\\INIT_3")));
+ cnt++;
+ }
+ }
+ }
+ log_header(design, "Updated %d cells with determined init value.\n", cnt);
+ }
+} DetermineInitPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/gowin/dram.txt b/techlibs/gowin/dram.txt
new file mode 100644
index 00000000..9db53025
--- /dev/null
+++ b/techlibs/gowin/dram.txt
@@ -0,0 +1,17 @@
+bram $__GW1NR_RAM16S4
+ init 1
+ abits 4
+ dbits 4
+ groups 2
+ ports 1 1
+ wrmode 0 1
+ enable 0 1
+ transp 0 1
+ clocks 0 1
+ clkpol 0 1
+endbram
+
+match $__GW1NR_RAM16S4
+ make_outreg
+ min wports 1
+endmatch
diff --git a/techlibs/gowin/drams_map.v b/techlibs/gowin/drams_map.v
new file mode 100644
index 00000000..a50ab365
--- /dev/null
+++ b/techlibs/gowin/drams_map.v
@@ -0,0 +1,31 @@
+module \$__GW1NR_RAM16S4 (CLK1, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
+ parameter CFG_ABITS = 4;
+ parameter CFG_DBITS = 4;
+
+ parameter [63:0] INIT = 64'bx;
+ input CLK1;
+
+ input [CFG_ABITS-1:0] A1ADDR;
+ output [CFG_DBITS-1:0] A1DATA;
+ input A1EN;
+
+ input [CFG_ABITS-1:0] B1ADDR;
+ input [CFG_DBITS-1:0] B1DATA;
+ input B1EN;
+
+ `include "brams_init3.vh"
+
+ RAM16S4
+ #(.INIT_0(INIT_0),
+ .INIT_1(INIT_1),
+ .INIT_2(INIT_2),
+ .INIT_3(INIT_3))
+ _TECHMAP_REPLACE_
+ (.AD(B1ADDR),
+ .DI(B1DATA),
+ .DO(A1DATA),
+ .CLK(CLK1),
+ .WRE(B1EN));
+
+
+endmodule
diff --git a/techlibs/gowin/synth_gowin.cc b/techlibs/gowin/synth_gowin.cc
index 9a3fcdbb..ac3dbfb2 100644
--- a/techlibs/gowin/synth_gowin.cc
+++ b/techlibs/gowin/synth_gowin.cc
@@ -49,9 +49,15 @@ struct SynthGowinPass : public ScriptPass
log(" from label is synonymous to 'begin', and empty to label is\n");
log(" synonymous to the end of the command list.\n");
log("\n");
+ log(" -nodffe\n");
+ log(" do not use flipflops with CE in output netlist\n");
+ log("\n");
log(" -nobram\n");
log(" do not use BRAM cells in output netlist\n");
log("\n");
+ log(" -nodram\n");
+ log(" do not use distributed RAM cells in output netlist\n");
+ log("\n");
log(" -noflatten\n");
log(" do not flatten design before synthesis\n");
log("\n");
@@ -65,7 +71,7 @@ struct SynthGowinPass : public ScriptPass
}
string top_opt, vout_file;
- bool retime, flatten, nobram;
+ bool retime, nobram, nodram, flatten, nodffe;
void clear_flags() YS_OVERRIDE
{
@@ -73,7 +79,9 @@ struct SynthGowinPass : public ScriptPass
vout_file = "";
retime = false;
flatten = true;
- nobram = true;
+ nobram = false;
+ nodffe = false;
+ nodram = false;
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -108,6 +116,14 @@ struct SynthGowinPass : public ScriptPass
nobram = true;
continue;
}
+ if (args[argidx] == "-nodram") {
+ nodram = true;
+ continue;
+ }
+ if (args[argidx] == "-nodffe") {
+ nodffe = true;
+ continue;
+ }
if (args[argidx] == "-noflatten") {
flatten = false;
continue;
@@ -147,25 +163,43 @@ struct SynthGowinPass : public ScriptPass
{
run("synth -run coarse");
}
- if (!nobram && check_label("bram", "(skip if -nobram)"))
+
+ if (!nobram && check_label("bram", "(skip if -nobram)"))
{
run("memory_bram -rules +/gowin/bram.txt");
- run("techmap -map +/gowin/brams_map.v");
+ run("techmap -map +/gowin/brams_map.v -map +/gowin/cells_sim.v");
+ }
+
+ if (!nodram && check_label("dram", "(skip if -nodram)"))
+ {
+ run("memory_bram -rules +/gowin/dram.txt");
+ run("techmap -map +/gowin/drams_map.v");
+ run("determine_init");
}
+
if (check_label("fine"))
{
run("opt -fast -mux_undef -undriven -fine");
run("memory_map");
run("opt -undriven -fine");
run("techmap -map +/techmap.v -map +/gowin/arith_map.v");
- run("opt -fine");
- run("clean -purge");
- run("splitnets -ports");
- run("setundef -undriven -zero");
+ run("techmap -map +/techmap.v");
if (retime || help_mode)
run("abc -dff", "(only if -retime)");
}
+ if (check_label("map_ffs"))
+ {
+ run("dffsr2dff");
+ run("dff2dffs");
+ run("opt_clean");
+ if (!nodffe)
+ run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*");
+ run("techmap -map +/gowin/cells_map.v");
+ run("opt_expr -mux_undef");
+ run("simplemap");
+ }
+
if (check_label("map_luts"))
{
run("abc -lut 4");
@@ -176,8 +210,10 @@ struct SynthGowinPass : public ScriptPass
{
run("techmap -map +/gowin/cells_map.v");
run("hilomap -hicell VCC V -locell GND G");
- run("iopadmap -inpad IBUF O:I -outpad OBUF I:O");
- run("clean -purge");
+ run("iopadmap -bits -inpad IBUF O:I -outpad OBUF I:O", "(unless -noiopads)");
+ run("dffinit -ff DFF Q INIT");
+ run("clean");
+
}
if (check_label("check"))
diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v
index 62a28364..f9945b2b 100644
--- a/techlibs/ice40/cells_sim.v
+++ b/techlibs/ice40/cells_sim.v
@@ -27,18 +27,27 @@ module SB_IO (