summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Undheim <ruben.undheim@gmail.com>2019-03-28 23:35:03 +0100
committerRuben Undheim <ruben.undheim@gmail.com>2019-03-28 23:35:03 +0100
commitff5734b20220e6fb4a3913cf5279ed94bb5156ea (patch)
tree4c438282926d7bac304ad3ad6ad89523c4c1d784
parentdb3c67fd6e140893450a44870ee9a75dd1f48b27 (diff)
Imported GIT HEAD: 0.8+20190328git32bd0f2
-rw-r--r--.editorconfig7
-rw-r--r--.github/issue_template.md16
-rw-r--r--.gitignore2
-rw-r--r--.travis.yml10
-rwxr-xr-x.travis/build-and-test.sh2
-rwxr-xr-x.travis/setup.sh50
-rw-r--r--CHANGELOG15
-rw-r--r--COPYING2
-rw-r--r--Makefile32
-rw-r--r--README.md53
-rw-r--r--backends/aiger/aiger.cc38
-rw-r--r--backends/btor/btor.cc118
-rw-r--r--backends/edif/edif.cc57
-rw-r--r--backends/firrtl/firrtl.cc284
-rw-r--r--backends/ilang/ilang_backend.cc2
-rw-r--r--backends/protobuf/protobuf.cc6
-rw-r--r--backends/simplec/simplec.cc2
-rw-r--r--backends/simplec/test00_uut.v6
-rw-r--r--backends/smt2/Makefile.inc18
-rw-r--r--backends/smt2/smt2.cc24
-rw-r--r--backends/smt2/smtbmc.py125
-rw-r--r--backends/smt2/smtio.py31
-rw-r--r--backends/smv/smv.cc83
-rw-r--r--backends/table/table.cc2
-rw-r--r--backends/verilog/verilog_backend.cc189
-rw-r--r--examples/anlogic/.gitignore7
-rw-r--r--examples/anlogic/README12
-rwxr-xr-xexamples/anlogic/build.sh4
-rw-r--r--examples/anlogic/build.tcl11
-rw-r--r--examples/anlogic/demo.adc2
-rw-r--r--examples/anlogic/demo.v18
-rw-r--r--examples/anlogic/demo.ys3
-rw-r--r--examples/igloo2/.gitignore4
-rw-r--r--examples/igloo2/example.pdc20
-rw-r--r--examples/igloo2/example.sdc2
-rw-r--r--examples/igloo2/example.v64
-rw-r--r--examples/igloo2/libero.tcl57
-rw-r--r--examples/igloo2/runme.sh6
-rw-r--r--frontends/aiger/Makefile.inc3
-rw-r--r--frontends/aiger/aigerparse.cc414
-rw-r--r--frontends/aiger/aigerparse.h51
-rw-r--r--frontends/ast/ast.cc331
-rw-r--r--frontends/ast/ast.h18
-rw-r--r--frontends/ast/genrtlil.cc205
-rw-r--r--frontends/ast/simplify.cc286
-rw-r--r--frontends/blif/blifparse.cc4
-rw-r--r--frontends/ilang/ilang_frontend.cc30
-rw-r--r--frontends/ilang/ilang_frontend.h2
-rw-r--r--frontends/ilang/ilang_parser.y42
-rw-r--r--frontends/liberty/liberty.cc15
-rw-r--r--frontends/verific/README31
-rw-r--r--frontends/verific/verific.cc534
-rw-r--r--frontends/verific/verific.h1
-rw-r--r--frontends/verific/verificsva.cc23
-rw-r--r--frontends/verilog/verilog_frontend.cc52
-rw-r--r--frontends/verilog/verilog_frontend.h9
-rw-r--r--frontends/verilog/verilog_lexer.l21
-rw-r--r--frontends/verilog/verilog_parser.y387
-rw-r--r--kernel/celltypes.h66
-rw-r--r--kernel/consteval.h9
-rw-r--r--kernel/driver.cc18
-rw-r--r--kernel/hashlib.h6
-rw-r--r--kernel/log.cc4
-rw-r--r--kernel/log.h6
-rw-r--r--kernel/register.cc2
-rw-r--r--kernel/rtlil.cc34
-rw-r--r--kernel/rtlil.h78
-rw-r--r--kernel/yosys.cc49
-rw-r--r--libs/ezsat/ezminisat.h2
-rw-r--r--libs/subcircuit/README6
-rw-r--r--manual/CHAPTER_CellLib.tex67
-rw-r--r--manual/CHAPTER_Overview.tex4
-rw-r--r--misc/launcher.c358
-rw-r--r--misc/yosys.proto12
-rw-r--r--passes/cmds/Makefile.inc2
-rw-r--r--passes/cmds/bugpoint.cc369
-rw-r--r--passes/cmds/chformal.cc4
-rw-r--r--passes/cmds/connect.cc6
-rw-r--r--passes/cmds/rename.cc148
-rw-r--r--passes/cmds/select.cc60
-rw-r--r--passes/cmds/setundef.cc22
-rw-r--r--passes/cmds/show.cc2
-rw-r--r--passes/cmds/tee.cc4
-rw-r--r--passes/equiv/Makefile.inc2
-rw-r--r--passes/equiv/equiv_make.cc86
-rw-r--r--passes/equiv/equiv_opt.cc157
-rw-r--r--passes/fsm/fsm_detect.cc8
-rw-r--r--passes/fsm/fsm_extract.cc4
-rw-r--r--passes/fsm/fsm_opt.cc3
-rw-r--r--passes/hierarchy/hierarchy.cc279
-rw-r--r--passes/hierarchy/uniquify.cc2
-rw-r--r--passes/memory/memory_bram.cc44
-rw-r--r--passes/memory/memory_collect.cc3
-rw-r--r--passes/memory/memory_dff.cc2
-rw-r--r--passes/opt/Makefile.inc1
-rw-r--r--passes/opt/opt_expr.cc240
-rw-r--r--passes/opt/opt_lut.cc607
-rw-r--r--passes/opt/opt_muxtree.cc18
-rw-r--r--passes/opt/opt_rmdff.cc13
-rw-r--r--passes/opt/share.cc8
-rw-r--r--passes/opt/wreduce.cc129
-rw-r--r--passes/pmgen/.gitignore1
-rw-r--r--passes/pmgen/Makefile.inc8
-rw-r--r--passes/pmgen/README.md224
-rw-r--r--passes/pmgen/ice40_dsp.cc237
-rw-r--r--passes/pmgen/ice40_dsp.pmg160
-rw-r--r--passes/pmgen/pmgen.py486
-rw-r--r--passes/proc/proc_clean.cc44
-rw-r--r--passes/sat/Makefile.inc4
-rw-r--r--passes/sat/async2sync.cc53
-rw-r--r--passes/sat/cutpoint.cc168
-rw-r--r--passes/sat/fmcombine.cc341
-rw-r--r--passes/sat/mutate.cc956
-rw-r--r--passes/sat/supercover.cc92
-rw-r--r--passes/techmap/Makefile.inc1
-rw-r--r--passes/techmap/abc.cc93
-rw-r--r--passes/techmap/deminout.cc7
-rw-r--r--passes/techmap/dff2dffe.cc35
-rw-r--r--passes/techmap/dffinit.cc33
-rw-r--r--passes/techmap/dfflibmap.cc16
-rw-r--r--passes/techmap/flowmap.cc1613
-rw-r--r--passes/techmap/libparse.cc176
-rw-r--r--passes/techmap/libparse.h10
-rw-r--r--passes/techmap/lut2mux.cc2
-rw-r--r--passes/tests/flowmap/flow.v22
-rw-r--r--passes/tests/flowmap/flowp.v16
-rw-r--r--passes/tests/flowmap/pack1.v11
-rw-r--r--passes/tests/flowmap/pack1p.v11
-rw-r--r--passes/tests/flowmap/pack2.v15
-rw-r--r--passes/tests/flowmap/pack2p.v15
-rw-r--r--passes/tests/flowmap/pack3.v15
-rw-r--r--passes/tests/flowmap/pack3p.v15
-rwxr-xr-xtechlibs/achronix/speedster22i/cells_map.v16
-rwxr-xr-xtechlibs/achronix/speedster22i/cells_sim.v8
-rwxr-xr-xtechlibs/achronix/synth_achronix.cc6
-rw-r--r--techlibs/anlogic/Makefile.inc12
-rw-r--r--techlibs/anlogic/anlogic_determine_init.cc72
-rw-r--r--techlibs/anlogic/anlogic_eqn.cc113
-rw-r--r--techlibs/anlogic/arith_map.v84
-rw-r--r--techlibs/anlogic/cells_map.v61
-rw-r--r--techlibs/anlogic/cells_sim.v103
-rw-r--r--techlibs/anlogic/dram_init_16x4.vh16
-rw-r--r--techlibs/anlogic/drams.txt16
-rw-r--r--techlibs/anlogic/drams_map.v22
-rw-r--r--techlibs/anlogic/eagle_bb.v1028
-rw-r--r--techlibs/anlogic/synth_anlogic.cc213
-rw-r--r--techlibs/common/Makefile.inc3
-rw-r--r--techlibs/common/cmp2lut.v105
-rw-r--r--techlibs/common/gate2lut.v87
-rw-r--r--techlibs/common/prep.cc2
-rw-r--r--techlibs/common/simcells.v4
-rw-r--r--techlibs/common/synth.cc48
-rw-r--r--techlibs/coolrunner2/synth_coolrunner2.cc4
-rw-r--r--techlibs/easic/synth_easic.cc2
-rw-r--r--techlibs/ecp5/.gitignore9
-rw-r--r--techlibs/ecp5/Makefile.inc38
-rw-r--r--techlibs/ecp5/arith_map.v2
-rw-r--r--techlibs/ecp5/bram.txt29
-rwxr-xr-xtechlibs/ecp5/brams_connect.py46
-rwxr-xr-xtechlibs/ecp5/brams_init.py22
-rw-r--r--techlibs/ecp5/brams_map.v115
-rw-r--r--techlibs/ecp5/cells_bb.v666
-rw-r--r--techlibs/ecp5/cells_map.v35
-rw-r--r--techlibs/ecp5/cells_sim.v154
-rw-r--r--techlibs/ecp5/dram.txt1
-rw-r--r--techlibs/ecp5/ecp5_ffinit.cc203
-rw-r--r--techlibs/ecp5/latches_map.v11
-rw-r--r--techlibs/ecp5/synth_ecp5.cc19
-rw-r--r--techlibs/gowin/Makefile.inc1
-rw-r--r--techlibs/gowin/arith_map.v59
-rw-r--r--techlibs/gowin/cells_sim.v6
-rw-r--r--techlibs/gowin/synth_gowin.cc31
-rw-r--r--techlibs/greenpak4/cells_map.v44
-rw-r--r--techlibs/greenpak4/synth_greenpak4.cc2
-rw-r--r--techlibs/ice40/Makefile.inc2
-rw-r--r--techlibs/ice40/brams_map.v4
-rw-r--r--techlibs/ice40/cells_sim.v279
-rw-r--r--techlibs/ice40/ice40_braminit.cc159
-rw-r--r--techlibs/ice40/ice40_ffssr.cc3
-rw-r--r--techlibs/ice40/ice40_opt.cc15
-rw-r--r--techlibs/ice40/ice40_unlut.cc106
-rw-r--r--techlibs/ice40/synth_ice40.cc81
-rw-r--r--techlibs/ice40/tests/.gitignore13
-rw-r--r--techlibs/ice40/tests/test_dsp_map.sh107
-rw-r--r--techlibs/ice40/tests/test_dsp_model.sh11
-rw-r--r--techlibs/ice40/tests/test_dsp_model.v342
-rw-r--r--techlibs/intel/common/brams_map.v16
-rw-r--r--techlibs/intel/cycloneive/arith_map.v10
-rw-r--r--techlibs/intel/cyclonev/cells_map.v4
-rw-r--r--techlibs/intel/cyclonev/cells_sim.v2
-rw-r--r--techlibs/intel/synth_intel.cc4
-rw-r--r--techlibs/sf2/Makefile.inc8
-rw-r--r--techlibs/sf2/arith_map.v21
-rw-r--r--techlibs/sf2/cells_map.v82
-rw-r--r--techlibs/sf2/cells_sim.v327
-rw-r--r--techlibs/sf2/sf2_iobs.cc197
-rw-r--r--techlibs/sf2/synth_sf2.cc246
-rw-r--r--techlibs/xilinx/Makefile.inc3
-rw-r--r--techlibs/xilinx/arith_map.v282
-rw-r--r--techlibs/xilinx/cells_map.v104
-rw-r--r--techlibs/xilinx/cells_sim.v65
-rw-r--r--techlibs/xilinx/cells_xtra.sh20
-rw-r--r--techlibs/xilinx/cells_xtra.v660
-rw-r--r--techlibs/xilinx/ff_map.v42
-rw-r--r--techlibs/xilinx/lut2lut.v65
-rw-r--r--techlibs/xilinx/lut_map.v94
-rw-r--r--techlibs/xilinx/synth_xilinx.cc77
-rw-r--r--tests/aiger/and.aag5
-rw-r--r--tests/aiger/and.aig3
-rw-r--r--tests/aiger/buffer.aag3
-rw-r--r--tests/aiger/buffer.aig2
-rw-r--r--tests/aiger/cnt1.aag3
-rw-r--r--tests/aiger/cnt1.aig3
-rw-r--r--tests/aiger/cnt1e.aag8
-rw-r--r--tests/aiger/cnt1e.aig4
-rw-r--r--tests/aiger/empty.aag1
-rw-r--r--tests/aiger/empty.aig1
-rw-r--r--tests/aiger/false.aag2
-rw-r--r--tests/aiger/false.aig2
-rw-r--r--tests/aiger/halfadder.aag14
-rw-r--r--tests/aiger/halfadder.aig9
-rw-r--r--tests/aiger/inverter.aag3
-rw-r--r--tests/aiger/inverter.aig2
-rw-r--r--tests/aiger/notcnt1.aag4
-rw-r--r--tests/aiger/notcnt1.aig4
-rw-r--r--tests/aiger/notcnt1e.aag8
-rw-r--r--tests/aiger/notcnt1e.aig4
-rw-r--r--tests/aiger/or.aag5
-rw-r--r--tests/aiger/or.aig3
-rwxr-xr-xtests/aiger/run-test.sh24
-rw-r--r--tests/aiger/toggle-re.aag14
-rw-r--r--tests/aiger/toggle-re.aig8
-rw-r--r--tests/aiger/toggle.aag4
-rw-r--r--tests/aiger/toggle.aig4
-rw-r--r--tests/aiger/true.aag2
-rw-r--r--tests/aiger/true.aig2
-rw-r--r--tests/asicworld/code_hdl_models_misc1.v22
-rw-r--r--tests/asicworld/code_hdl_models_mux21_switch.v22
-rw-r--r--tests/asicworld/code_hdl_models_nand_switch.v14
-rw-r--r--tests/asicworld/code_hdl_models_t_gate_switch.v11
-rwxr-xr-xtests/asicworld/run-test.sh2
-rw-r--r--tests/asicworld/xfirrtl23
-rw-r--r--tests/errors/syntax_err01.v4
-rw-r--r--tests/errors/syntax_err02.v7
-rw-r--r--tests/errors/syntax_err03.v7
-rw-r--r--tests/errors/syntax_err04.v4
-rw-r--r--tests/errors/syntax_err05.v4
-rw-r--r--tests/errors/syntax_err06.v6
-rw-r--r--tests/errors/syntax_err07.v6
-rw-r--r--tests/errors/syntax_err08.v6
-rw-r--r--tests/errors/syntax_err09.v3
-rw-r--r--tests/errors/syntax_err10.v3
-rw-r--r--tests/errors/syntax_err11.v3
-rw-r--r--tests/errors/syntax_err12.v7
-rw-r--r--tests/errors/syntax_err13.v4
-rw-r--r--tests/liberty/.gitignore2
-rw-r--r--tests/liberty/busdef.lib81
-rw-r--r--tests/liberty/normal.lib359
-rw-r--r--tests/liberty/processdefs.lib48
-rwxr-xr-xtests/liberty/run-test.sh10
-rw-r--r--tests/liberty/semicolextra.lib48
-rw-r--r--tests/liberty/semicolmissing.lib72
-rw-r--r--tests/liberty/small.v16
-rw-r--r--tests/lut/.gitignore1
-rw-r--r--tests/lut/check_map.ys6
-rw-r--r--tests/lut/map_and.v5
-rw-r--r--tests/lut/map_cmp.v29
-rw-r--r--tests/lut/map_mux.v5
-rw-r--r--tests/lut/map_not.v5
-rw-r--r--tests/lut/map_or.v5
-rw-r--r--tests/lut/map_xor.v5
-rwxr-xr-xtests/lut/run-test.sh6
-rw-r--r--tests/opt/.gitignore1
-rw-r--r--tests/opt/opt_expr_cmp.v40
-rw-r--r--tests/opt/opt_expr_cmp.ys4
-rw-r--r--tests/opt/opt_ff.v21
-rw-r--r--tests/opt/opt_ff.ys3
-rw-r--r--tests/opt/opt_lut.v18
-rw-r--r--tests/opt/opt_lut.ys4
-rw-r--r--tests/opt/opt_lut_elim.il19
-rw-r--r--tests/opt/opt_lut_elim.ys3
-rw-r--r--tests/opt/opt_lut_port.il18
-rw-r--r--tests/opt/opt_lut_port.ys3
-rwxr-xr-xtests/opt/run-test.sh6
-rw-r--r--tests/simple/dff_init.v42
-rw-r--r--tests/simple/generate.v56
-rw-r--r--tests/simple/hierdefparam.v2
-rw-r--r--tests/simple/task_func.v19
-rw-r--r--tests/simple/xfirrtl26
-rw-r--r--tests/sva/basic01.sv2
-rw-r--r--tests/sva/extnets.sv22
-rw-r--r--tests/svinterfaces/.gitignore8
-rwxr-xr-xtests/svinterfaces/run-test.sh6
-rwxr-xr-xtests/svinterfaces/runone.sh44
-rw-r--r--tests/svinterfaces/svinterface1.sv117
-rw-r--r--tests/svinterfaces/svinterface1_ref.v107
-rw-r--r--tests/svinterfaces/svinterface1_tb.v57
-rw-r--r--tests/svinterfaces/svinterface_at_top.sv125
-rw-r--r--tests/svinterfaces/svinterface_at_top_ref.v120
-rw-r--r--tests/svinterfaces/svinterface_at_top_tb.v68
-rw-r--r--tests/svinterfaces/svinterface_at_top_tb_wrapper.v68
-rw-r--r--tests/svinterfaces/svinterface_at_top_wrapper.v33
-rw-r--r--tests/tools/autotest.mk6
-rwxr-xr-xtests/tools/autotest.sh61
-rw-r--r--tests/various/hierarchy.sh59
-rwxr-xr-xtests/various/run-test.sh10
306 files changed, 19211 insertions, 1635 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..4d6f5ef7
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,7 @@
+root = true
+
+[*]
+indent_style = tab
+indent_size = tab
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/.github/issue_template.md b/.github/issue_template.md
index 24e91a4e..5a0723c3 100644
--- a/.github/issue_template.md
+++ b/.github/issue_template.md
@@ -1,9 +1,20 @@
## Steps to reproduce the issue
*Provide instructions for reproducing the issue. Make sure to include
-all neccessary source files. (You can simply drag&drop a .zip file into
+all necessary source files. (You can simply drag&drop a .zip file into
the issue editor.)*
+Also, make sure that the issue is actually reproducable in current git
+master of Yosys.
+
+See https://stackoverflow.com/help/mcve for some information on how to
+create a Minimal, Complete, and Verifiable example (MCVE).
+
+Please do not waste our time with issues that lack sufficient information
+to reproduce the issue easily. We will simply close those issues.
+
+Contact https://www.symbioticeda.com/ if you need commercial support for Yosys.
+
## Expected behavior
*Please describe the behavior you would have expected from the tool.*
@@ -11,6 +22,3 @@ the issue editor.)*
## Actual behavior
*Please describe how the behavior you see differs from the expected behavior.*
-
-**Important Note:** Nobody will be able to help you and/or fix the issue if you
-do not provide sufficient information for reproducing the problem.
diff --git a/.gitignore b/.gitignore
index 48ce458c..e24f7975 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,8 @@
/yosys-abc.exe
/yosys-config
/yosys-smtbmc
+/yosys-smtbmc.exe
+/yosys-smtbmc-script.py
/yosys-filterlib
/yosys-filterlib.exe
/kernel/version_*.cc
diff --git a/.travis.yml b/.travis.yml
index 321c3325..7c6e4e43 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -132,11 +132,11 @@ matrix:
env:
- MATRIX_EVAL="CONFIG=clang && CC=clang-5.0 && CXX=clang++-5.0"
- # Latest clang on Mac OS X
- - os: osx
- osx_image: xcode8
- env:
- - MATRIX_EVAL="CONFIG=gcc && CC=gcc-7 && CXX=g++-7"
+# # Latest clang on Mac OS X
+# - os: osx
+# osx_image: xcode9.4
+# env:
+# - MATRIX_EVAL="CONFIG=clang && CC=clang && CXX=clang++"
before_install:
- ./.travis/setup.sh
diff --git a/.travis/build-and-test.sh b/.travis/build-and-test.sh
index 096dde64..b8c35041 100755
--- a/.travis/build-and-test.sh
+++ b/.travis/build-and-test.sh
@@ -36,6 +36,8 @@ echo
##########################################################################
+./yosys tests/simple/fiedler-cooley.v
+
echo
echo 'Testing...' && echo -en 'travis_fold:start:script.test\\r'
echo
diff --git a/.travis/setup.sh b/.travis/setup.sh
index 06851578..4af0b8ee 100755
--- a/.travis/setup.sh
+++ b/.travis/setup.sh
@@ -6,48 +6,15 @@ source .travis/common.sh
##########################################################################
-# Fixing Travis's git clone
-echo
-echo 'Fixing git setup...' && echo -en 'travis_fold:start:before_install.git\\r'
-echo
-git fetch --unshallow && git fetch --tags
-
-# For pull requests, we get more info about the git source.
-if [ z"$TRAVIS_PULL_REQUEST_SLUG" != z ]; then
- echo "- Fetching from pull request source"
- git remote add source https://github.com/$TRAVIS_PULL_REQUEST_SLUG.git
- git fetch source && git fetch --tags
-
- echo "- Fetching the actual pull request"
- git fetch origin pull/$TRAVIS_PULL_REQUEST/head:pull-$TRAVIS_PULL_REQUEST-head
- git fetch origin pull/$TRAVIS_PULL_REQUEST/merge:pull-$TRAVIS_PULL_REQUEST-merge
-
- git log -n 5 --graph pull-$TRAVIS_PULL_REQUEST-merge
-fi
-
-# For building branches we need to fix the "detached head" state.
-if [ z"$TRAVIS_BRANCH" != z ]; then
- TRAVIS_COMMIT_ACTUAL=$(git log --pretty=format:'%H' -n 1)
- echo "- Fixing detached head (current $TRAVIS_COMMIT_ACTUAL -> $TRAVIS_COMMIT)"
- git remote -v
- git branch -v
- if [ x"$(git show-ref -s HEAD)" = x"$TRAVIS_COMMIT" ]; then
- echo "Checked out at $TRAVIS_COMMIT"
- else
- if [ z"$TRAVIS_PULL_REQUEST_SLUG" != z ]; then
- git fetch source $TRAVIS_COMMIT || echo "Unable to fetch $TRAVIS_COMMIT from source"
- fi
- git fetch origin $TRAVIS_COMMIT || echo "Unable to fetch $TRAVIS_COMMIT from origin"
- fi
- git branch -D $TRAVIS_BRANCH || true
- git checkout $TRAVIS_COMMIT -b $TRAVIS_BRANCH
- git branch -v
-fi
-
# Output status information.
-git status
-git describe --tags
-git log -n 5 --graph
+(
+ set +e
+ set -x
+ git status
+ git branch -v
+ git log -n 5 --graph
+ git log --format=oneline -n 20 --graph
+)
echo
echo -en 'travis_fold:end:before_install.git\\r'
echo
@@ -64,7 +31,6 @@ if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
brew tap Homebrew/bundle
brew bundle
brew install ccache
- brew install gcc@7
echo
echo -en 'travis_fold:end:before_install.brew\\r'
echo
diff --git a/CHANGELOG b/CHANGELOG
index 5499c309..42e01645 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,6 +3,21 @@ List of major changes and improvements between releases
=======================================================
+Yosys 0.8 .. Yosys 0.8-dev
+--------------------------
+
+ * Various
+ - Added $changed support to read_verilog
+ - Added "write_edif -attrprop"
+ - Added "ice40_unlut" pass
+ - Added "opt_lut" pass
+ - Added "synth_ice40 -relut"
+ - Added "synth_ice40 -noabc"
+ - Added "gate2lut.v" techmap rule
+ - Added "rename -src"
+ - Added "equiv_opt" pass
+
+
Yosys 0.7 .. Yosys 0.8
----------------------
diff --git a/COPYING b/COPYING
index a01b7b69..a121cdfe 100644
--- a/COPYING
+++ b/COPYING
@@ -1,4 +1,4 @@
-Copyright (C) 2012 - 2017 Clifford Wolf <clifford@clifford.at>
+Copyright (C) 2012 - 2018 Clifford Wolf <clifford@clifford.at>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/Makefile b/Makefile
index d759b114..8586e976 100644
--- a/Makefile
+++ b/Makefile
@@ -10,6 +10,7 @@ CONFIG := clang
# features (the more the better)
ENABLE_TCL := 1
ENABLE_ABC := 1
+ENABLE_GLOB := 1
ENABLE_PLUGINS := 1
ENABLE_READLINE := 1
ENABLE_EDITLINE := 0
@@ -72,6 +73,7 @@ PKG_CONFIG ?= pkg-config
SED ?= sed
BISON ?= bison
STRIP ?= strip
+AWK ?= awk
ifeq ($(OS), Darwin)
PLUGIN_LDFLAGS += -undefined dynamic_lookup
@@ -99,7 +101,7 @@ LDFLAGS += -rdynamic
LDLIBS += -lrt
endif
-YOSYS_VER := 0.8
+YOSYS_VER := 0.8+$(shell cd $(YOSYS_SRC) && test -e .git && { git log --author=clifford@clifford.at --oneline 4d4665b.. 2> /dev/null | wc -l; })
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
OBJS = kernel/version_$(GIT_REV).o
@@ -109,7 +111,7 @@ OBJS = kernel/version_$(GIT_REV).o
# is just a symlink to your actual ABC working directory, as 'make mrproper'
# will remove the 'abc' directory and you do not want to accidentally
# delete your work on ABC..
-ABCREV = ae6716b
+ABCREV = 2ddc57d
ABCPULL = 1
ABCURL ?= https://github.com/berkeley-abc/abc
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1
@@ -160,7 +162,7 @@ ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H"
else ifeq ($(CONFIG),gcc-static)
LD = $(CXX)
LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -static
-LDLIBS := $(filter-out -lrt,$(LDLIBS))
+LDLIBS := $(filter-out -lrt,$(LDLIBS))
CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS))
CXXFLAGS += -std=c++11 -Os
ABCMKARGS = CC="$(CC)" CXX="$(CXX)" LD="$(LD)" ABC_USE_LIBSTDCXX=1 LIBS="-lm -lpthread -static" OPTFLAGS="-O" \
@@ -297,6 +299,10 @@ LDLIBS += -ldl
endif
endif
+ifeq ($(ENABLE_GLOB),1)
+CXXFLAGS += -DYOSYS_ENABLE_GLOB
+endif
+
ifeq ($(ENABLE_TCL),1)
TCL_VERSION ?= tcl$(shell bash -c "tclsh <(echo 'puts [info tclversion]')")
ifeq ($(OS), FreeBSD)
@@ -356,11 +362,15 @@ endif
endif
ifeq ($(ENABLE_VERIFIC),1)
-VERIFIC_DIR ?= /usr/local/src/verific_lib_eval
-VERIFIC_COMPONENTS ?= verilog vhdl database util containers sdf hier_tree
+VERIFIC_DIR ?= /usr/local/src/verific_lib
+VERIFIC_COMPONENTS ?= verilog vhdl database util containers hier_tree
CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC
+ifeq ($(OS), Darwin)
+LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)) -lz
+else
LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -lz
endif
+endif
ifeq ($(ENABLE_PROTOBUF),1)
LDLIBS += $(shell pkg-config --cflags --libs protobuf)
@@ -391,8 +401,8 @@ endef
ifeq ($(PRETTY), 1)
P_STATUS = 0
P_OFFSET = 0
-P_UPDATE = $(eval P_STATUS=$(shell echo $(OBJS) yosys$(EXE) | gawk 'BEGIN { RS = " "; I = $(P_STATUS)+0; } $$1 == "$@" && NR > I { I = NR; } END { print I; }'))
-P_SHOW = [$(shell gawk "BEGIN { N=$(words $(OBJS) yosys$(EXE)); printf \"%3d\", $(P_OFFSET)+90*$(P_STATUS)/N; exit; }")%]
+P_UPDATE = $(eval P_STATUS=$(shell echo $(OBJS) yosys$(EXE) | $(AWK) 'BEGIN { RS = " "; I = $(P_STATUS)+0; } $$1 == "$@" && NR > I { I = NR; } END { print I; }'))
+P_SHOW = [$(shell $(AWK) "BEGIN { N=$(words $(OBJS) yosys$(EXE)); printf \"%3d\", $(P_OFFSET)+90*$(P_STATUS)/N; exit; }")%]
P = @echo "$(if $(findstring $@,$(TARGETS) $(EXTRA_TARGETS)),$(eval P_OFFSET = 10))$(call P_UPDATE)$(call P_SHOW) Building $@";
Q = @
S = -s
@@ -565,7 +575,7 @@ test: $(TARGETS) $(EXTRA_TARGETS)
+cd tests/simple && bash run-test.sh $(SEEDOPT)
+cd tests/hana && bash run-test.sh $(SEEDOPT)
+cd tests/asicworld && bash run-test.sh $(SEEDOPT)
- +cd tests/realmath && bash run-test.sh $(SEEDOPT)
+ # +cd tests/realmath && bash run-test.sh $(SEEDOPT)
+cd tests/share && bash run-test.sh $(SEEDOPT)
+cd tests/fsm && bash run-test.sh $(SEEDOPT)
+cd tests/techmap && bash run-test.sh
@@ -573,6 +583,9 @@ test: $(TARGETS) $(EXTRA_TARGETS)
+cd tests/bram && bash run-test.sh $(SEEDOPT)
+cd tests/various && bash run-test.sh
+cd tests/sat && bash run-test.sh
+ +cd tests/svinterfaces && bash run-test.sh $(SEEDOPT)
+ +cd tests/opt && bash run-test.sh
+ +cd tests/aiger && bash run-test.sh
@echo ""
@echo " Passed \"make test\"."
@echo ""
@@ -594,7 +607,7 @@ vloghtb: $(TARGETS) $(EXTRA_TARGETS)
ystests: $(TARGETS) $(EXTRA_TARGETS)
rm -rf tests/ystests
git clone https://github.com/YosysHQ/yosys-tests.git tests/ystests
- +PATH="$$PWD:$$PATH" cd tests/ystests && $(MAKE)
+ +$(MAKE) PATH="$$PWD:$$PATH" -C tests/ystests
@echo ""
@echo " Finished \"make ystests\"."
@echo ""
@@ -655,6 +668,7 @@ clean:
rm -rf tests/sat/*.log tests/techmap/*.log tests/various/*.log
rm -rf tests/bram/temp tests/fsm/temp tests/realmath/temp tests/share/temp tests/smv/temp
rm -rf vloghtb/Makefile vloghtb/refdat vloghtb/rtl vloghtb/scripts vloghtb/spec vloghtb/check_yosys vloghtb/vloghammer_tb.tar.bz2 vloghtb/temp vloghtb/log_test_*
+ rm -f tests/svinterfaces/*.log_stdout tests/svinterfaces/*.log_stderr tests/svinterfaces/dut_result.txt tests/svinterfaces/reference_result.txt tests/svinterfaces/a.out tests/svinterfaces/*_syn.v tests/svinterfaces/*.diff
rm -f tests/tools/cmp_tbdata
clean-abc:
diff --git a/README.md b/README.md
index 41ae4ac1..4048ecbc 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
```
yosys -- Yosys Open SYnthesis Suite
-Copyright (C) 2012 - 2017 Clifford Wolf <clifford@clifford.at>
+Copyright (C) 2012 - 2018 Clifford Wolf <clifford@clifford.at>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -34,11 +34,24 @@ compatible license that is similar in terms to the MIT license
or the 2-clause BSD license).
-Web Site
-========
+Web Site and Other Resources
+============================
More information and documentation can be found on the Yosys web site:
-http://www.clifford.at/yosys/
+- http://www.clifford.at/yosys/
+
+The "Documentation" page on the web site contains links to more resources,
+including a manual that even describes some of the Yosys internals:
+- http://www.clifford.at/yosys/documentation.html
+
+The file `CodingReadme` in this directory contains additional information
+for people interested in using the Yosys C++ APIs.
+
+Users interested in formal verification might want to use the formal verification
+front-end for Yosys, SymbiYosys:
+- https://symbiyosys.readthedocs.io/en/latest/
+- https://github.com/YosysHQ/SymbiYosys
+
Setup
======
@@ -92,12 +105,15 @@ Makefile.
To build Yosys simply type 'make' in this directory.
$ make
- $ make test
$ sudo make install
Note that this also downloads, builds and installs ABC (using yosys-abc
as executable name).
+Tests are located in the tests subdirectory and can be executed using the test target. Note that you need gawk as well as a recent version of iverilog (i.e. build from git). Then, execute tests via:
+
+ $ make test
+
Getting Started
===============
@@ -117,7 +133,7 @@ reading the design using the Verilog frontend:
yosys> read_verilog tests/simple/fiedler-cooley.v
-writing the design to the console in yosys's internal format:
+writing the design to the console in Yosys's internal format:
yosys> write_ilang
@@ -234,7 +250,7 @@ Unsupported Verilog-2005 Features
=================================
The following Verilog-2005 features are not supported by
-yosys and there are currently no plans to add support
+Yosys and there are currently no plans to add support
for them:
- Non-synthesizable language features as defined in
@@ -285,9 +301,9 @@ Verilog Attributes and non-standard features
storage element. The register itself will always have all bits set
to 'x' (undefined). The variable may only be used as blocking assigned
temporary variable within an always block. This is mostly used internally
- by yosys to synthesize Verilog functions and access arrays.
+ by Yosys to synthesize Verilog functions and access arrays.
-- The ``onehot`` attribute on wires mark them as onehot state register. This
+- The ``onehot`` attribute on wires mark them as one-hot state register. This
is used for example for memory port sharing and set by the fsm_map pass.
- The ``blackbox`` attribute on modules is used to mark empty stub modules
@@ -296,6 +312,12 @@ Verilog Attributes and non-standard features
passes to identify input and output ports of cells. The Verilog backend
also does not output blackbox modules on default.
+- 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
+ (HDL) name of a module when renaming a module.
+
- The ``keep`` attribute on cells and wires is used to mark objects that should
never be removed by the optimizer. This is used for example for cells that
have hidden connections that are not part of the netlist, such as IO pads.
@@ -319,13 +341,13 @@ 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.
-- In addition to the ``(* ... *)`` attribute syntax, yosys supports
+- In addition to the ``(* ... *)`` attribute syntax, Yosys supports
the non-standard ``{* ... *}`` attribute syntax to set default attributes
for everything that comes after the ``{* ... *}`` statement. (Reset
by adding an empty ``{* *}`` statement.)
- In module parameter and port declarations, and cell port and parameter
- lists, a trailing comma is ignored. This simplifies writing verilog code
+ lists, a trailing comma is ignored. This simplifies writing Verilog code
generators a bit in some cases.
- Modules can be declared with ``module mod_name(...);`` (with three dots
@@ -410,11 +432,11 @@ Non-standard or SystemVerilog features for formal verification
- The system functions ``$allconst`` and ``$allseq`` can be used to construct
formal exist-forall problems. Assumptions only hold if the trace satisfies
- the assumtion for all ``$allconst/$allseq`` values. For assertions and cover
+ the assumption for all ``$allconst/$allseq`` values. For assertions and cover
statements it is sufficient if just one ``$allconst/$allseq`` value triggers
the property (similar to ``$anyconst/$anyseq``).
-- Wires/registers decalred using the ``anyconst/anyseq/allconst/allseq`` attribute
+- Wires/registers declared using the ``anyconst/anyseq/allconst/allseq`` attribute
(for example ``(* anyconst *) reg [7:0] foobar;``) will behave as if driven
by a ``$anyconst/$anyseq/$allconst/$allseq`` function.
@@ -452,6 +474,9 @@ from SystemVerilog:
into a design with ``read_verilog``, all its packages are available to
SystemVerilog files being read into the same design afterwards.
+- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
+ ports are inputs or outputs are supported.
+
Building the documentation
==========================
@@ -482,6 +507,6 @@ Then execute, from the root of the repository:
Notes:
-- To run `make manual` you need to have installed yosys with `make install`,
+- To run `make manual` you need to have installed Yosys with `make install`,
otherwise it will fail on finding `kernel/yosys.h` while building
`PRESENTATION_Prog`.
diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc
index c323691a..dfe506c6 100644
--- a/backends/aiger/aiger.cc
+++ b/backends/aiger/aiger.cc
@@ -100,7 +100,7 @@ struct AigerWriter
return aig_map.at(bit);
}
- AigerWriter(Module *module, bool zinit_mode) : module(module), zinit_mode(zinit_mode), sigmap(module)
+ AigerWriter(Module *module, bool zinit_mode, bool imode, bool omode, bool bmode) : module(module), zinit_mode(zinit_mode), sigmap(module)
{
pool<SigBit> undriven_bits;
pool<SigBit> unused_bits;
@@ -293,6 +293,10 @@ struct AigerWriter
aig_map[bit] = 2*aig_m;
}
+ if (imode && input_bits.empty()) {
+ aig_m++, aig_i++;
+ }
+
if (zinit_mode)
{
for (auto it : ff_map) {
@@ -371,6 +375,11 @@ struct AigerWriter
aig_outputs.push_back(bit2aig(bit));
}
+ if (omode && output_bits.empty()) {
+ aig_o++;
+ aig_outputs.push_back(0);
+ }
+
for (auto it : asserts) {
aig_b++;
int bit_a = bit2aig(it.first);
@@ -378,6 +387,11 @@ struct AigerWriter
aig_outputs.push_back(mkgate(bit_a^1, bit_en));
}
+ if (bmode && asserts.empty()) {
+ aig_b++;
+ aig_outputs.push_back(0);
+ }
+
for (auto it : assumes) {
aig_c++;
int bit_a = bit2aig(it.first);
@@ -689,6 +703,11 @@ struct AigerBackend : public Backend {
log(" -vmap <filename>\n");
log(" like -map, but more verbose\n");
log("\n");
+ log(" -I, -O, -B\n");
+ log(" If the design contains no input/output/assert then create one\n");
+ log(" dummy input/output/bad_state pin to make the tools reading the\n");
+ log(" AIGER file happy.\n");
+ log("\n");
}
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
@@ -697,6 +716,9 @@ struct AigerBackend : public Backend {
bool miter_mode = false;
bool symbols_mode = false;
bool verbose_map = false;
+ bool imode = false;
+ bool omode = false;
+ bool bmode = false;
std::string map_filename;
log_header(design, "Executing AIGER backend.\n");
@@ -729,6 +751,18 @@ struct AigerBackend : public Backend {
verbose_map = true;
continue;
}
+ if (args[argidx] == "-I") {
+ imode = true;
+ continue;
+ }
+ if (args[argidx] == "-O") {
+ omode = true;
+ continue;
+ }
+ if (args[argidx] == "-B") {
+ bmode = true;
+ continue;
+ }
break;
}
extra_args(f, filename, args, argidx);
@@ -738,7 +772,7 @@ struct AigerBackend : public Backend {
if (top_module == nullptr)
log_error("Can't find top module in current design!\n");
- AigerWriter writer(top_module, zinit_mode);
+ AigerWriter writer(top_module, zinit_mode, imode, omode, bmode);
writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode);
if (!map_filename.empty()) {
diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc
index 58d2a862..55c49499 100644
--- a/backends/btor/btor.cc
+++ b/backends/btor/btor.cc
@@ -133,12 +133,13 @@ struct BtorWorker
cell_recursion_guard.insert(cell);
btorf_push(log_id(cell));
- if (cell->type.in("$add", "$sub", "$and", "$or", "$xor", "$xnor", "$shl", "$sshl", "$shr", "$sshr", "$shift", "$shiftx",
- "$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_"))
+ if (cell->type.in("$add", "$sub", "$mul", "$and", "$or", "$xor", "$xnor", "$shl", "$sshl", "$shr", "$sshr", "$shift", "$shiftx",
+ "$concat", "$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_"))
{
string btor_op;
if (cell->type == "$add") btor_op = "add";
if (cell->type == "$sub") btor_op = "sub";
+ if (cell->type == "$mul") btor_op = "mul";
if (cell->type.in("$shl", "$sshl")) btor_op = "sll";
if (cell->type == "$shr") btor_op = "srl";
if (cell->type == "$sshr") btor_op = "sra";
@@ -146,6 +147,7 @@ struct BtorWorker
if (cell->type.in("$and", "$_AND_")) btor_op = "and";
if (cell->type.in("$or", "$_OR_")) btor_op = "or";
if (cell->type.in("$xor", "$_XOR_")) btor_op = "xor";
+ if (cell->type == "$concat") btor_op = "concat";
if (cell->type == "$_NAND_") btor_op = "nand";
if (cell->type == "$_NOR_") btor_op = "nor";
if (cell->type.in("$xnor", "$_XNOR_")) btor_op = "xnor";
@@ -214,6 +216,40 @@ struct BtorWorker
goto okay;
}
+ if (cell->type.in("$div", "$mod"))
+ {
+ string btor_op;
+ if (cell->type == "$div") btor_op = "div";
+ if (cell->type == "$mod") btor_op = "rem";
+ log_assert(!btor_op.empty());
+
+ int width = GetSize(cell->getPort("\\Y"));
+ width = std::max(width, GetSize(cell->getPort("\\A")));
+ width = std::max(width, GetSize(cell->getPort("\\B")));
+
+ bool a_signed = cell->hasParam("\\A_SIGNED") ? cell->getParam("\\A_SIGNED").as_bool() : false;
+ bool b_signed = cell->hasParam("\\B_SIGNED") ? cell->getParam("\\B_SIGNED").as_bool() : false;
+
+ int nid_a = get_sig_nid(cell->getPort("\\A"), width, a_signed);
+ int nid_b = get_sig_nid(cell->getPort("\\B"), width, b_signed);
+
+ int sid = get_bv_sid(width);
+ int nid = next_nid++;
+ btorf("%d %c%s %d %d %d\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b);
+
+ SigSpec sig = sigmap(cell->getPort("\\Y"));
+
+ if (GetSize(sig) < width) {
+ int sid = get_bv_sid(GetSize(sig));
+ int nid2 = next_nid++;
+ btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1);
+ nid = nid2;
+ }
+
+ add_nid_sig(nid, sig);
+ goto okay;
+ }
+
if (cell->type.in("$_ANDNOT_", "$_ORNOT_"))
{
int sid = get_bv_sid(1);
@@ -506,6 +542,18 @@ struct BtorWorker
}
}
+ Const initval;
+ for (int i = 0; i < GetSize(sig_q); i++)
+ if (initbits.count(sig_q[i]))
+ initval.bits.push_back(initbits.at(sig_q[i]) ? State::S1 : State::S0);
+ else
+ initval.bits.push_back(State::Sx);
+
+ int nid_init_val = -1;
+
+ if (!initval.is_fully_undef())
+ nid_init_val = get_sig_nid(initval);
+
int sid = get_bv_sid(GetSize(sig_q));
int nid = next_nid++;
@@ -514,15 +562,7 @@ struct BtorWorker
else
btorf("%d state %d %s\n", nid, sid, log_id(symbol));
- Const initval;
- for (int i = 0; i < GetSize(sig_q); i++)
- if (initbits.count(sig_q[i]))
- initval.bits.push_back(initbits.at(sig_q[i]) ? State::S1 : State::S0);
- else
- initval.bits.push_back(State::Sx);
-
- if (!initval.is_fully_undef()) {
- int nid_init_val = get_sig_nid(initval);
+ if (nid_init_val >= 0) {
int nid_init = next_nid++;
if (verbose)
btorf("; initval = %s\n", log_signal(initval));
@@ -575,6 +615,7 @@ struct BtorWorker
{
int abits = cell->getParam("\\ABITS").as_int();
int width = cell->getParam("\\WIDTH").as_int();
+ int nwords = cell->getParam("\\SIZE").as_int();
int rdports = cell->getParam("\\RD_PORTS").as_int();
int wrports = cell->getParam("\\WR_PORTS").as_int();
@@ -601,6 +642,52 @@ struct BtorWorker
int data_sid = get_bv_sid(width);
int bool_sid = get_bv_sid(1);
int sid = get_mem_sid(abits, width);
+
+ Const initdata = cell->getParam("\\INIT");
+ initdata.exts(nwords*width);
+ int nid_init_val = -1;
+
+ if (!initdata.is_fully_undef())
+ {
+ bool constword = true;
+ Const firstword = initdata.extract(0, width);
+
+ for (int i = 1; i < nwords; i++) {
+ Const thisword = initdata.extract(i*width, width);
+ if (thisword != firstword) {
+ constword = false;
+ break;
+ }
+ }
+
+ if (constword)
+ {
+ if (verbose)
+ btorf("; initval = %s\n", log_signal(firstword));
+ nid_init_val = get_sig_nid(firstword);
+ }
+ else
+ {
+ int nid_init_val = next_nid++;
+ btorf("%d state %d\n", nid_init_val, sid);
+
+ for (int i = 0; i < nwords; i++) {
+ Const thisword = initdata.extract(i*width, width);
+ if (thisword.is_fully_undef())
+ continue;
+ Const thisaddr(i, abits);
+ int nid_thisword = get_sig_nid(thisword);
+ int nid_thisaddr = get_sig_nid(thisaddr);
+ int last_nid_init_val = nid_init_val;
+ nid_init_val = next_nid++;
+ if (verbose)
+ btorf("; initval[%d] = %s\n", i, log_signal(thisword));
+ btorf("%d write %d %d %d %d\n", nid_init_val, sid, last_nid_init_val, nid_thisaddr, nid_thisword);
+ }
+ }
+ }
+
+
int nid = next_nid++;
int nid_head = nid;
@@ -609,6 +696,12 @@ struct BtorWorker
else
btorf("%d state %d %s\n", nid, sid, log_id(cell));
+ if (nid_init_val >= 0)
+ {
+ int nid_init = next_nid++;
+ btorf("%d init %d %d %d\n", nid_init, sid, nid, nid_init_val);
+ }
+
if (asyncwr)
{
for (int port = 0; port < wrports; port++)
@@ -892,9 +985,8 @@ struct BtorWorker
btorf_push(stringf("output %s", log_id(wire)));
- int sid = get_bv_sid(GetSize(wire));
int nid = get_sig_nid(wire);
- btorf("%d output %d %d %s\n", next_nid++, sid, nid, log_id(wire));
+ btorf("%d output %d %s\n", next_nid++, nid, log_id(wire));
btorf_pop(stringf("output %s", log_id(wire)));
}
diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc
index 5f9ec54f..7e30b67a 100644
--- a/backends/edif/edif.cc
+++ b/backends/edif/edif.cc
@@ -106,6 +106,13 @@ struct EdifBackend : public Backend {
log(" if the design contains constant nets. use \"hilomap\" to map to custom\n");
log(" constant drivers first)\n");
log("\n");
+ log(" -gndvccy\n");
+ log(" create \"GND\" and \"VCC\" cells with \"Y\" outputs. (the default is \"G\"\n");
+ log(" for \"GND\" and \"P\" for \"VCC\".)\n");
+ log("\n");
+ log(" -attrprop\n");
+ log(" create EDIF properties for cell attributes\n");
+ log("\n");
log(" -pvector {par|bra|ang}\n");
log(" sets the delimiting character for module port rename clauses to\n");
log(" parentheses, square brackets, or angle brackets.\n");
@@ -121,8 +128,9 @@ struct EdifBackend : public Backend {
log_header(design, "Executing EDIF backend.\n");
std::string top_module_name;
bool port_rename = false;
+ bool attr_properties = false;
std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports;
- bool nogndvcc = false;
+ bool nogndvcc = false, gndvccy = false;
CellTypes ct(design);
EdifNames edif_names;
@@ -137,6 +145,14 @@ struct EdifBackend : public Backend {
nogndvcc = true;
continue;
}
+ if (args[argidx] == "-gndvccy") {
+ gndvccy = true;
+ continue;
+ }
+ if (args[argidx] == "-attrprop") {
+ attr_properties = true;
+ continue;
+ }
if (args[argidx] == "-pvector" && argidx+1 < args.size()) {
std::string parray;
port_rename = true;
@@ -203,7 +219,7 @@ struct EdifBackend : public Backend {
*f << stringf(" (cellType GENERIC)\n");
*f << stringf(" (view VIEW_NETLIST\n");
*f << stringf(" (viewType NETLIST)\n");
- *f << stringf(" (interface (port G (direction OUTPUT)))\n");
+ *f << stringf(" (interface (port %c (direction OUTPUT)))\n", gndvccy ? 'Y' : 'G');
*f << stringf(" )\n");
*f << stringf(" )\n");
@@ -211,7 +227,7 @@ struct EdifBackend : public Backend {
*f << stringf(" (cellType GENERIC)\n");
*f << stringf(" (view VIEW_NETLIST\n");
*f << stringf(" (viewType NETLIST)\n");
- *f << stringf(" (interface (port P (direction OUTPUT)))\n");
+ *f << stringf(" (interface (port %c (direction OUTPUT)))\n", gndvccy ? 'Y' : 'P');
*f << stringf(" )\n");
*f << stringf(" )\n");
}
@@ -332,24 +348,33 @@ struct EdifBackend : public Backend {
*f << stringf(" (instance %s\n", EDIF_DEF(cell->name));
*f << stringf(" (viewRef VIEW_NETLIST (cellRef %s%s))", EDIF_REF(cell->type),
lib_cell_ports.count(cell->type) > 0 ? " (libraryRef LIB)" : "");
- for (auto &p : cell->parameters)
- if ((p.second.flags & RTLIL::CONST_FLAG_STRING) != 0)
- *f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(p.first), p.second.decode_string().c_str());
- else if (p.second.bits.size() <= 32 && RTLIL::SigSpec(p.second).is_fully_def())
- *f << stringf("\n (property %s (integer %u))", EDIF_DEF(p.first), p.second.as_int());
+
+ auto add_prop = [&](IdString name, Const val) {
+ if ((val.flags & RTLIL::CONST_FLAG_STRING) != 0)
+ *f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(name), val.decode_string().c_str());
+ else if (val.bits.size() <= 32 && RTLIL::SigSpec(val).is_fully_def())
+ *f << stringf("\n (property %s (integer %u))", EDIF_DEF(name), val.as_int());
else {
std::string hex_string = "";
- for (size_t i = 0; i < p.second.bits.size(); i += 4) {
+ for (size_t i = 0; i < val.bits.size(); i += 4) {
int digit_value = 0;
- if (i+0 < p.second.bits.size() && p.second.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1;
- if (i+1 < p.second.bits.size() && p.second.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2;
- if (i+2 < p.second.bits.size() && p.second.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4;
- if (i+3 < p.second.bits.size() && p.second.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8;
+ if (i+0 < val.bits.size() && val.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1;
+ if (i+1 < val.bits.size() && val.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2;
+ if (i+2 < val.bits.size() && val.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4;
+ if (i+3 < val.bits.size() && val.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8;
char digit_str[2] = { "0123456789abcdef"[digit_value], 0 };
hex_string = std::string(digit_str) + hex_string;
}
- *f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(p.first), GetSize(p.second.bits), hex_string.c_str());
+ *f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(name), GetSize(val.bits), hex_string.c_str());
}
+ };
+
+ for (auto &p : cell->parameters)
+ add_prop(p.first, p.second);
+ if (attr_properties)
+ for (auto &p : cell->attributes)
+ add_prop(p.first, p.second);
+
*f << stringf(")\n");
for (auto &p : cell->connections()) {
RTLIL::SigSpec sig = sigmap(p.second);
@@ -403,9 +428,9 @@ struct EdifBackend : public Backend {
if (nogndvcc)
log_error("Design contains constant nodes (map with \"hilomap\" first).\n");
if (sig == RTLIL::State::S0)
- *f << stringf(" (portRef G (instanceRef GND))\n");
+ *f << stringf(" (portRef %c (instanceRef GND))\n", gndvccy ? 'Y' : 'G');
if (sig == RTLIL::State::S1)
- *f << stringf(" (portRef P (instanceRef VCC))\n");
+ *f << stringf(" (portRef %c (instanceRef VCC))\n", gndvccy ? 'Y' : 'P');
}
*f << stringf(" ))\n");
}
diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc
index 32410a65..eef6401b 100644
--- a/backends/firrtl/firrtl.cc
+++ b/backends/firrtl/firrtl.cc
@@ -23,7 +23,11 @@
#include "kernel/celltypes.h"
#include "kernel/cellaigs.h"
#include "kernel/log.h"
+#include <algorithm>
#include <string>
+#include <regex>
+#include <vector>
+#include <cmath>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -37,6 +41,7 @@ static const FDirection FD_NODIRECTION = 0x0;
static const FDirection FD_IN = 0x1;
static const FDirection FD_OUT = 0x2;
static const FDirection FD_INOUT = 0x3;
+static const int FIRRTL_MAX_DSH_WIDTH_ERROR = 20; // For historic reasons, this is actually one greater than the maximum allowed shift width
// Get a port direction with respect to a specific module.
FDirection getPortFDirection(IdString id, Module *module)
@@ -160,11 +165,9 @@ struct FirrtlWorker
std::string fid(RTLIL::IdString internal_id)
{
- const char *str = internal_id.c_str();
- return *str == '\\' ? str + 1 : str;
+ return make_id(internal_id);
}
-
std::string cellname(RTLIL::Cell *cell)
{
return fid(cell->name).c_str();
@@ -173,6 +176,26 @@ struct FirrtlWorker
void process_instance(RTLIL::Cell *cell, vector<string> &wire_exprs)
{
std::string cell_type = fid(cell->type);
+ std::string instanceOf;
+ // If this is a parameterized module, its parent module is encoded in the cell type
+ if (cell->type.substr(0, 8) == "$paramod")
+ {
+ std::string::iterator it;
+ for (it = cell_type.begin(); it < cell_type.end(); it++)
+ {
+ switch (*it) {
+ case '\\': /* FALL_THROUGH */
+ case '=': /* FALL_THROUGH */
+ case '\'': /* FALL_THROUGH */
+ case '$': instanceOf.append("_"); break;
+ default: instanceOf.append(1, *it); break;
+ }
+ }
+ }
+ else
+ {
+ instanceOf = cell_type;
+ }
std::string cell_name = cellname(cell);
std::string cell_name_comment;
@@ -182,41 +205,74 @@ struct FirrtlWorker
cell_name_comment = "";
// Find the module corresponding to this instance.
auto instModule = design->module(cell->type);
- wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), cell_type.c_str()));
+ // If there is no instance for this, just return.
+ if (instModule == NULL)
+ {
+ log_warning("No instance for %s.%s\n", cell_type.c_str(), cell_name.c_str());
+ return;
+ }
+ wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceOf.c_str()));
for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
if (it->second.size() > 0) {
const SigSpec &secondSig = it->second;
const std::string firstName = cell_name + "." + make_id(it->first);
- const std::string secondName = make_expr(secondSig);
+ const std::string secondExpr = make_expr(secondSig);
// Find the direction for this port.
FDirection dir = getPortFDirection(it->first, instModule);
- std::string source, sink;
+ std::string sourceExpr, sinkExpr;
+ const SigSpec *sinkSig = nullptr;
switch (dir) {
case FD_INOUT:
- log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", log_id(cell_type), log_signal(it->second));
+ log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", cell_type.c_str(), log_signal(it->second));
case FD_OUT:
- source = firstName;
- sink = secondName;
+ sourceExpr = firstName;
+ sinkExpr = secondExpr;
+ sinkSig = &secondSig;
break;
case FD_NODIRECTION:
- log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", log_id(cell_type), log_signal(it->second));
+ log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", cell_type.c_str(), log_signal(it->second));
/* FALL_THROUGH */
case FD_IN:
- source = secondName;
- sink = firstName;
+ sourceExpr = secondExpr;
+ sinkExpr = firstName;
break;
default:
- log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", log_id(cell_type), log_signal(it->second), dir);
+ log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", cell_type.c_str(), log_signal(it->second), dir);
break;
}
- wire_exprs.push_back(stringf("\n%s%s <= %s", indent.c_str(), sink.c_str(), source.c_str()));
+ // Check for subfield assignment.
+ std::string bitsString = "bits(";
+ if (sinkExpr.substr(0, bitsString.length()) == bitsString ) {
+ if (sinkSig == nullptr)
+ log_error("Unknown subfield %s.%s\n", cell_type.c_str(), sinkExpr.c_str());
+ // Don't generate the assignment here.
+ // Add the source and sink to the "reverse_wire_map" and we'll output the assignment
+ // as part of the coalesced subfield assignments for this wire.
+ register_reverse_wire_map(sourceExpr, *sinkSig);
+ } else {
+ wire_exprs.push_back(stringf("\n%s%s <= %s", indent.c_str(), sinkExpr.c_str(), sourceExpr.c_str()));
+ }
}
}
wire_exprs.push_back(stringf("\n"));
}
+ // Given an expression for a shift amount, and a maximum width,
+ // generate the FIRRTL expression for equivalent dynamic shift taking into account FIRRTL shift semantics.
+ std::string gen_dshl(const string b_expr, const int b_padded_width)
+ {
+ string result = b_expr;
+ if (b_padded_width >= FIRRTL_MAX_DSH_WIDTH_ERROR) {
+ int max_shift_width_bits = FIRRTL_MAX_DSH_WIDTH_ERROR - 1;
+ string max_shift_string = stringf("UInt<%d>(%d)", max_shift_width_bits, (1<<max_shift_width_bits) - 1);
+ // Deal with the difference in semantics between FIRRTL and verilog
+ result = stringf("mux(gt(%s, %s), %s, bits(%s, %d, 0))", b_expr.c_str(), max_shift_string.c_str(), max_shift_string.c_str(), b_expr.c_str(), max_shift_width_bits - 1);
+ }
+ return result;
+ }
+
void run()
{
f << stringf(" module %s:\n", make_id(module->name));
@@ -225,6 +281,12 @@ struct FirrtlWorker
for (auto wire : module->wires())
{
const auto wireName = make_id(wire->name);
+ // If a wire has initial data, issue a warning since FIRRTL doesn't currently support it.
+ if (wire->attributes.count("\\init")) {
+ log_warning("Initial value (%s) for (%s.%s) not supported\n",
+ wire->attributes.at("\\init").as_string().c_str(),
+ log_id(module), log_id(wire));
+ }
if (wire->port_id)
{
if (wire->port_input && wire->port_output)
@@ -240,7 +302,8 @@ struct FirrtlWorker
for (auto cell : module->cells())
{
- // Is this cell is a module instance?
+ bool extract_y_bits = false; // Assume no extraction of final bits will be required.
+ // Is this cell is a module instance?
if (cell->type[0] != '$')
{
process_instance(cell, wire_exprs);
@@ -264,21 +327,21 @@ struct FirrtlWorker
}
string primop;
- bool always_uint = false;
+ bool always_uint = false;
if (cell->type == "$not") primop = "not";
- if (cell->type == "$neg") primop = "neg";
- if (cell->type == "$logic_not") {
+ else if (cell->type == "$neg") primop = "neg";
+ else if (cell->type == "$logic_not") {
primop = "eq";
a_expr = stringf("%s, UInt(0)", a_expr.c_str());
}
- if (cell->type == "$reduce_and") primop = "andr";
- if (cell->type == "$reduce_or") primop = "orr";
- if (cell->type == "$reduce_xor") primop = "xorr";
- if (cell->type == "$reduce_xnor") {
+ else if (cell->type == "$reduce_and") primop = "andr";
+ else if (cell->type == "$reduce_or") primop = "orr";
+ else if (cell->type == "$reduce_xor") primop = "xorr";
+ else if (cell->type == "$reduce_xnor") {
primop = "not";
a_expr = stringf("xorr(%s)", a_expr.c_str());
}
- if (cell->type == "$reduce_bool") {
+ else if (cell->type == "$reduce_bool") {
primop = "neq";
// Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand.
bool a_signed = cell->parameters.at("\\A_SIGNED").as_bool();
@@ -297,14 +360,15 @@ struct FirrtlWorker
continue;
}
if (cell->type.in("$add", "$sub", "$mul", "$div", "$mod", "$xor", "$and", "$or", "$eq", "$eqx",
- "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl",
- "$logic_and", "$logic_or"))
+ "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl",
+ "$logic_and", "$logic_or"))
{
string y_id = make_id(cell->name);
bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool();
int y_width = cell->parameters.at("\\Y_WIDTH").as_int();
string a_expr = make_expr(cell->getPort("\\A"));
string b_expr = make_expr(cell->getPort("\\B"));
+ int b_padded_width = cell->parameters.at("\\B_WIDTH").as_int();
wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
if (cell->parameters.at("\\A_SIGNED").as_bool()) {
@@ -315,67 +379,93 @@ struct FirrtlWorker
if (cell->parameters.at("\\B_SIGNED").as_bool()) {
b_expr = "asSInt(" + b_expr + ")";
}
- b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width);
+ if (b_padded_width < y_width) {
+ auto b_sig = cell->getPort("\\B");
+ b_padded_width = y_width;
+ }
}
- a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
+ auto a_sig = cell->getPort("\\A");
if (cell->parameters.at("\\A_SIGNED").as_bool() & (cell->type == "$shr")) {
a_expr = "asUInt(" + a_expr + ")";
}
string primop;
- bool always_uint = false;
+ bool always_uint = false;
if (cell->type == "$add") primop = "add";
- if (cell->type == "$sub") primop = "sub";
- if (cell->type == "$mul") primop = "mul";
- if (cell->type == "$div") primop = "div";
- if (cell->type == "$mod") primop = "rem";
- if (cell->type == "$and") {
+ else if (cell->type == "$sub") primop = "sub";
+ else if (cell->type == "$mul") primop = "mul";
+ else if (cell->type == "$div") primop = "div";
+ else if (cell->type == "$mod") primop = "rem";
+ else if (cell->type == "$and") {
primop = "and";
always_uint = true;
}
- if (cell->type == "$or" ) {
+ else if (cell->type == "$or" ) {
primop = "or";
always_uint = true;
}
- if (cell->type == "$xor") {
+ else if (cell->type == "$xor") {
primop = "xor";
always_uint = true;
}
- if ((cell->type == "$eq") | (cell->type == "$eqx")) {
+ else if ((cell->type == "$eq") | (cell->type == "$eqx")) {
primop = "eq";
always_uint = true;
}
- if ((cell->type == "$ne") | (cell->type == "$nex")) {
+ else if ((cell->type == "$ne") | (cell->type == "$nex")) {
primop = "neq";
always_uint = true;
}
- if (cell->type == "$gt") {
+ else if (cell->type == "$gt") {
primop = "gt";
always_uint = true;
}
- if (cell->type == "$ge") {
+ else if (cell->type == "$ge") {
primop = "geq";
always_uint = true;
}
- if (cell->type == "$lt") {
+ else if (cell->type == "$lt") {
primop = "lt";
always_uint = true;
}
- if (cell->type == "$le") {
+ else if (cell->type == "$le") {
primop = "leq";
always_uint = true;
}
- if ((cell->type == "$shl") | (cell->type == "$sshl")) primop = "dshl";
- if ((cell->type == "$shr") | (cell->type == "$sshr")) primop = "dshr";
- if ((cell->type == "$logic_and")) {
+ else if ((cell->type == "$shl") | (cell->type == "$sshl")) {
+ // FIRRTL will widen the result (y) by the amount of the shift.
+ // We'll need to offset this by extracting the un-widened portion as Verilog would do.
+ extract_y_bits = true;
+ // Is the shift amount constant?
+ auto b_sig = cell->getPort("\\B");
+ if (b_sig.is_fully_const()) {
+ primop = "shl";
+ } else {
+ primop = "dshl";
+ // Convert from FIRRTL left shift semantics.
+ b_expr = gen_dshl(b_expr, b_padded_width);
+ }
+ }
+ else if ((cell->type == "$shr") | (cell->type == "$sshr")) {
+ // We don't need to extract a specific range of bits.
+ extract_y_bits = false;
+ // Is the shift amount constant?
+ auto b_sig = cell->getPort("\\B");
+ if (b_sig.is_fully_const()) {
+ primop = "shr";
+ } else {
+ primop = "dshr";
+ }
+ }
+ else if ((cell->type == "$logic_and")) {
primop = "and";
a_expr = "neq(" + a_expr + ", UInt(0))";
b_expr = "neq(" + b_expr + ", UInt(0))";
always_uint = true;
}
- if ((cell->type == "$logic_or")) {
+ else if ((cell->type == "$logic_or")) {
primop = "or";
a_expr = "neq(" + a_expr + ", UInt(0))";
b_expr = "neq(" + b_expr + ", UInt(0))";
@@ -388,6 +478,11 @@ struct FirrtlWorker
string expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str());
+ // Deal with FIRRTL's "shift widens" semantics
+ if (extract_y_bits) {
+ expr = stringf("bits(%s, %d, 0)", expr.c_str(), y_width - 1);
+ }
+
if ((is_signed && !always_uint) || cell->type.in("$sub"))
expr = stringf("asUInt(%s)", expr.c_str());
@@ -513,7 +608,65 @@ struct FirrtlWorker
continue;
}
- log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
+ // This may be a parameterized module - paramod.
+ if (cell->type.substr(0, 8) == "$paramod")
+ {
+ process_instance(cell, wire_exprs);
+ continue;
+ }
+ if (cell->type == "$shiftx") {
+ // assign y = a[b +: y_width];
+ // We'll extract the correct bits as part of the primop.
+
+ string y_id = make_id(cell->name);
+ int y_width = cell->parameters.at("\\Y_WIDTH").as_int();
+ string a_expr = make_expr(cell->getPort("\\A"));
+ // Get the initial bit selector
+ string b_expr = make_expr(cell->getPort("\\B"));
+ wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
+
+ if (cell->getParam("\\B_SIGNED").as_bool()) {
+ // Use validif to constrain the selection (test the sign bit)
+ auto b_string = b_expr.c_str();
+ int b_sign = cell->parameters.at("\\B_WIDTH").as_int() - 1;
+ b_expr = stringf("validif(not(bits(%s, %d, %d)), %s)", b_string, b_sign, b_sign, b_string);
+ }
+ string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_expr.c_str());
+
+ cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
+ register_reverse_wire_map(y_id, cell->getPort("\\Y"));
+ continue;
+ }
+ if (cell->type == "$shift") {
+ // assign y = a >> b;
+ // where b may be negative
+
+ string y_id = make_id(cell->name);
+ int y_width = cell->parameters.at("\\Y_WIDTH").as_int();
+ string a_expr = make_expr(cell->getPort("\\A"));
+ string b_expr = make_expr(cell->getPort("\\B"));
+ auto b_string = b_expr.c_str();
+ int b_padded_width = cell->parameters.at("\\B_WIDTH").as_int();
+ string expr;
+ wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
+
+ if (cell->getParam("\\B_SIGNED").as_bool()) {
+ // We generate a left or right shift based on the sign of b.
+ std::string dshl = stringf("bits(dshl(%s, %s), 0, %d)", a_expr.c_str(), gen_dshl(b_expr, b_padded_width).c_str(), y_width);
+ std::string dshr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string);
+ expr = stringf("mux(%s < 0, %s, %s)",
+ b_string,
+ dshl.c_str(),
+ dshr.c_str()
+ );
+ } else {
+ expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string);
+ }
+ cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
+ register_reverse_wire_map(y_id, cell->getPort("\\Y"));
+ continue;
+ }
+ log_warning("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
}
for (auto conn : module->connections())
@@ -629,38 +782,53 @@ struct FirrtlBackend : public Backend {
log(" write_firrtl [options] [filename]\n");
log("\n");
log("Write a FIRRTL netlist of the current design.\n");
+ log("The following commands are executed by this command:\n");
+ log(" pmuxtree\n");
log("\n");
}
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
- size_t argidx;
- for (argidx = 1; argidx < args.size(); argidx++)
- {
- // if (args[argidx] == "-aig") {
- // aig_mode = true;
- // continue;
- // }
- break;
+ size_t argidx = args.size(); // We aren't expecting any arguments.
+
+ // If we weren't explicitly passed a filename, use the last argument (if it isn't a flag).
+ if (filename == "") {
+ if (argidx > 0 && args[argidx - 1][0] != '-') {
+ // extra_args and friends need to see this argument.
+ argidx -= 1;
+ filename = args[argidx];
+ }
}
extra_args(f, filename, args, argidx);
- log_header(design, "Executing FIRRTL backend.\n");
+ if (!design->full_selection())
+ log_cmd_error("This command only operates on fully selected designs!\n");
- Module *top = design->top_module();
+ log_header(design, "Executing FIRRTL backend.\n");
+ log_push();
- if (top == nullptr)
- log_error("No top module found!\n");
+ Pass::call(design, stringf("pmuxtree"));
namecache.clear();
autoid_counter = 0;
+ // Get the top module, or a reasonable facsimile - we need something for the circuit name.
+ Module *top = design->top_module();
+ Module *last = nullptr;
+ // Generate module and wire names.
for (auto module : design->modules()) {
make_id(module->name);
+ last = module;
+ if (top == nullptr && module->get_bool_attribute("\\top")) {
+ top = module;
+ }
for (auto wire : module->wires())
if (wire->port_id)
make_id(wire->name);
}
+ if (top == nullptr)
+ top = last;
+
*f << stringf("circuit %s:\n", make_id(top->name));
for (auto module : design->modules())
diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc
index 4c58ea08..dc39e5e0 100644
--- a/backends/ilang/ilang_backend.cc
+++ b/backends/ilang/ilang_backend.cc
@@ -204,7 +204,7 @@ void ILANG_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const
f << stringf("%s case ", indent.c_str());
for (size_t i = 0; i < (*it)->compare.size(); i++) {
if (i > 0)
- f << stringf(", ");
+ f << stringf(" , ");
dump_sigspec(f, (*it)->compare[i]);
}
f << stringf("\n");
diff --git a/backends/protobuf/protobuf.cc b/backends/protobuf/protobuf.cc
index f56147ce..549fc73a 100644
--- a/backends/protobuf/protobuf.cc
+++ b/backends/protobuf/protobuf.cc
@@ -48,7 +48,7 @@ struct ProtobufDesignSerializer
ProtobufDesignSerializer(bool use_selection, bool aig_mode) :
aig_mode_(aig_mode), use_selection_(use_selection) { }
-
+
string get_name(IdString name)
{
return RTLIL::unescape_id(name);
@@ -60,7 +60,7 @@ struct ProtobufDesignSerializer
{
for (auto &param : parameters) {
std::string key = get_name(param.first);
-
+
yosys::pb::Parameter pb_param;
@@ -207,7 +207,7 @@ struct ProtobufDesignSerializer
(*models)[aig.name] = pb_model;
}
}
-
+
void serialize_design(yosys::pb::Design *pb, Design *design)
{
GOOGLE_PROTOBUF_VERIFY_VERSION;
diff --git a/backends/simplec/simplec.cc b/backends/simplec/simplec.cc
index 349bc5a6..6f2ccbe2 100644
--- a/backends/simplec/simplec.cc
+++ b/backends/simplec/simplec.cc
@@ -748,7 +748,7 @@ struct SimplecBackend : public Backend {
log("\n");
log(" write_simplec [options] [filename]\n");
log("\n");
- log("Write simple C code for simulating the design. The C code writen can be used to\n");
+ log("Write simple C code for simulating the design. The C code written can be used to\n");
log("simulate the design in a C environment, but the purpose of this command is to\n");
log("generate code that works well with C-based formal verification.\n");
log("\n");
diff --git a/backends/simplec/test00_uut.v b/backends/simplec/test00_uut.v
index 744dbe9e..92329a6f 100644
--- a/backends/simplec/test00_uut.v
+++ b/backends/simplec/test00_uut.v
@@ -3,12 +3,12 @@ module test(input [31:0] a, b, c, output [31:0] x, y, z, w);
unit_y unit_y_inst (.a(a), .b(b), .c(c), .y(y));
assign z = a ^ b ^ c, w = z;
endmodule
-
+
module unit_x(input [31:0] a, b, c, output [31:0] x);
assign x = (a & b) | c;
endmodule
-
+
module unit_y(input [31:0] a, b, c, output [31:0] y);
assign y = a & (b | c);
endmodule
-
+
diff --git a/backends/smt2/Makefile.inc b/backends/smt2/Makefile.inc
index dce82f01..92941d4c 100644
--- a/backends/smt2/Makefile.inc
+++ b/backends/smt2/Makefile.inc
@@ -3,14 +3,30 @@ OBJS += backends/smt2/smt2.o
ifneq ($(CONFIG),mxe)
ifneq ($(CONFIG),emcc)
+
+# MSYS targets support yosys-smtbmc, but require a launcher script
+ifeq ($(CONFIG),$(filter $(CONFIG),msys2 msys2-64))
+TARGETS += yosys-smtbmc.exe yosys-smtbmc-script.py
+# Needed to find the Python interpreter for yosys-smtbmc scripts.
+# Override if necessary, it is only used for msys2 targets.
+PYTHON := $(shell cygpath -w -m $(PREFIX)/bin/python3)
+
+yosys-smtbmc-script.py: backends/smt2/smtbmc.py
+ $(P) sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' \
+ -e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@
+
+yosys-smtbmc.exe: misc/launcher.c yosys-smtbmc-script.py
+ $(P) gcc -DGUI=0 -O -s -o $@ $<
+# Other targets
+else
TARGETS += yosys-smtbmc
yosys-smtbmc: backends/smt2/smtbmc.py
$(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new
$(Q) chmod +x $@.new
$(Q) mv $@.new $@
+endif
$(eval $(call add_share_file,share/python3,backends/smt2/smtio.py))
endif
endif
-
diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc
index e2777ae0..688535f3 100644
--- a/backends/smt2/smt2.cc
+++ b/backends/smt2/smt2.cc
@@ -416,6 +416,7 @@ struct Smt2Worker
for (char ch : expr) {
if (ch == 'A') processed_expr += get_bv(sig_a);
else if (ch == 'B') processed_expr += get_bv(sig_b);
+ else if (ch == 'P') processed_expr += get_bv(cell->getPort("\\B"));
else if (ch == 'L') processed_expr += is_signed ? "a" : "l";
else if (ch == 'U') processed_expr += is_signed ? "s" : "u";
else processed_expr += ch;
@@ -554,7 +555,9 @@ struct Smt2Worker
if (cell->type.in("$shift", "$shiftx")) {
if (cell->getParam("\\B_SIGNED").as_bool()) {
- /* FIXME */
+ return export_bvop(cell, stringf("(ite (bvsge P #b%0*d) "
+ "(bvlshr A B) (bvlshr A (bvneg B)))",
+ GetSize(cell->getPort("\\B")), 0), 's');
} else {
return export_bvop(cell, "(bvlshr A B)", 's');
}
@@ -885,8 +888,8 @@ struct Smt2Worker
string name_a = get_bool(cell->getPort("\\A"));
string name_en = get_bool(cell->getPort("\\EN"));
- decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id,
- cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell)));
+ string infostr = (cell->name[0] == '$' && cell->attributes.count("\\src")) ? cell->attributes.at("\\src").decode_string() : get_id(cell);
+ decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id, infostr.c_str()));
if (cell->type == "$cover")
decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (and %s %s)) ; %s\n",
@@ -1101,20 +1104,27 @@ struct Smt2Worker
break;
Const initword = init_data.extract(i*width, width, State::Sx);
+ Const initmask = initword;
bool gen_init_constr = false;
- for (auto bit : initword.bits)
- if (bit == State::S0 || bit == State::S1)
+ for (int k = 0; k < GetSize(initword); k++) {
+ if (initword[k] == State::S0 || initword[k] == State::S1) {
gen_init_constr = true;
+ initmask[k] = State::S1;
+ } else {
+ initmask[k] = State::S0;
+ initword[k] = State::S0;
+ }
+ }
if (gen_init_constr)
{
if (statebv)
/* FIXME */;
else
- init_list.push_back(stringf("(= (select (|%s#%d#0| state) #b%s) #b%s) ; %s[%d]",
+ init_list.push_back(stringf("(= (bvand (select (|%s#%d#0| state) #b%s) #b%s) #b%s) ; %s[%d]",
get_id(module), arrayid, Const(i, abits).as_string().c_str(),
- initword.as_string().c_str(), get_id(cell), i));
+ initmask.as_string().c_str(), initword.as_string().c_str(), get_id(cell), i));
}
}
}
diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py
index 6af2a5ac..445a42e0 100644
--- a/backends/smt2/smtbmc.py
+++ b/backends/smt2/smtbmc.py
@@ -32,6 +32,7 @@ cexfile = None
aimfile = None
aiwfile = None
aigheader = True
+btorwitfile = None
vlogtbfile = None
vlogtbtop = None
inconstr = list()
@@ -86,12 +87,15 @@ yosys-smtbmc [options] <yosys_smt2_output>
--aig <aim_filename>:<aiw_filename>
like above, but for map files and witness files that do not
- share a filename prefix (or use differen file extensions).
+ share a filename prefix (or use different file extensions).
--aig-noheader
the AIGER witness file does not include the status and
properties lines.
+ --btorwit <btor_witness_filename>
+ read a BTOR witness.
+
--noinfo
only run the core proof, do not collect and print any
additional information (e.g. which assert failed)
@@ -99,8 +103,8 @@ yosys-smtbmc [options] <yosys_smt2_output>
--presat
check if the design with assumptions but without assertions
is SAT before checking if assertions are UNSAT. This will
- detect if there are contradicting assumtions. In some cases
- this will also help to "warmup" the solver, potentially
+ detect if there are contradicting assumptions. In some cases
+ this will also help to "warm up" the solver, potentially
yielding a speedup.
--final-only
@@ -145,14 +149,14 @@ yosys-smtbmc [options] <yosys_smt2_output>
--append <num_steps>
add <num_steps> time steps at the end of the trace
when creating a counter example (this additional time
- steps will still be constrained by assumtions)
+ steps will still be constrained by assumptions)
""" + so.helpmsg())
sys.exit(1)
try:
opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:igcm:", so.longopts +
- ["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "presat",
+ ["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "btorwit=", "presat",
"dump-vcd=", "dump-vlogtb=", "vlogtb-top=", "dump-smtc=", "dump-all", "noinfo", "append=",
"smtc-init", "smtc-top=", "noinit"])
except:
@@ -189,6 +193,8 @@ for o, a in opts:
aiwfile = a + ".aiw"
elif o == "--aig-noheader":
aigheader = False
+ elif o == "--btorwit":
+ btorwitfile = a
elif o == "--dump-vcd":
vcdfile = a
elif o == "--dump-vlogtb":
@@ -575,6 +581,103 @@ if aimfile is not None:
num_steps = max(num_steps, step+1)
step += 1
+if btorwitfile is not None:
+ with open(btorwitfile, "r") as f:
+ step = None
+ suffix = None
+ altsuffix = None
+ header_okay = False
+
+ for line in f:
+ line = line.strip()
+
+ if line == "sat":
+ header_okay = True
+ continue
+
+ if not header_okay:
+ continue
+
+ if line == "" or line[0] == "b" or line[0] == "j":
+ continue
+
+ if line == ".":
+ break
+
+ if line[0] == '#' or line[0] == '@':
+ step = int(line[1:])
+ suffix = line
+ altsuffix = suffix
+ if suffix[0] == "@":
+ altsuffix = "#" + suffix[1:]
+ else:
+ altsuffix = "@" + suffix[1:]
+ continue
+
+ line = line.split()
+
+ if len(line) == 0:
+ continue
+
+ if line[-1].endswith(suffix):
+ line[-1] = line[-1][0:len(line[-1]) - len(suffix)]
+
+ if line[-1].endswith(altsuffix):
+ line[-1] = line[-1][0:len(line[-1]) - len(altsuffix)]
+
+ if line[-1][0] == "$":
+ continue
+
+ # BV assignments
+ if len(line) == 3 and line[1][0] != "[":
+ value = line[1]
+ name = line[2]
+
+ path = smt.get_path(topmod, name)
+
+ if not smt.net_exists(topmod, path):
+ continue
+
+ width = smt.net_width(topmod, path)
+
+ if width == 1:
+ assert value in ["0", "1"]
+ value = "true" if value == "1" else "false"
+ else:
+ value = "#b" + value
+
+ smtexpr = "(= [%s] %s)" % (name, value)
+ constr_assumes[step].append((btorwitfile, smtexpr))
+
+ # Array assignments
+ if len(line) == 4 and line[1][0] == "[":
+ index = line[1]
+ value = line[2]
+ name = line[3]
+
+ path = smt.get_path(topmod, name)
+
+ if not smt.mem_exists(topmod, path):
+ continue
+
+ meminfo = smt.mem_info(topmod, path)
+
+ if meminfo[1] == 1:
+ assert value in ["0", "1"]
+ value = "true" if value == "1" else "false"
+ else:
+ value = "#b" + value
+
+ assert index[0] == "["
+ assert index[-1] == "]"
+ index = "#b" + index[1:-1]
+
+ smtexpr = "(= (select [%s] %s) %s)" % (name, index, value)
+ constr_assumes[step].append((btorwitfile, smtexpr))
+
+ skip_steps = step
+ num_steps = step+1
+
def write_vcd_trace(steps_start, steps_stop, index):
filename = vcdfile.replace("%", index)
print_msg("Writing trace to VCD file: %s" % (filename))
@@ -1259,7 +1362,11 @@ elif covermode:
smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i))
smt_assert_consequent(get_constr_expr(constr_assumes, i))
print_msg("Re-solving with appended steps..")
- assert smt_check_sat() == "sat"
+ if smt_check_sat() == "unsat":
+ print("%s Cannot appended steps without violating assumptions!" % smt.timestamp())
+ found_failed_assert = True
+ retstatus = False
+ break
reached_covers = smt.bv2bin(smt.get("(covers_%d s%d)" % (coveridx, step)))
assert len(reached_covers) == len(cover_desc)
@@ -1377,7 +1484,11 @@ else: # not tempind, covermode
smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i))
smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i))
smt_assert_consequent(get_constr_expr(constr_assumes, i))
- assert smt_check_sat() == "sat"
+ print_msg("Re-solving with appended steps..")
+ if smt_check_sat() == "unsat":
+ print("%s Cannot appended steps without violating assumptions!" % smt.timestamp())
+ retstatus = False
+ break
print_anyconsts(step)
for i in range(step, last_check_step+1):
print_failed_asserts(i)
diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py
index 3fc823e3..ab20a4af 100644
--- a/backends/smt2/smtio.py
+++ b/backends/smt2/smtio.py
@@ -31,11 +31,19 @@ from threading import Thread
# does not run out of stack frames when parsing large expressions
if os.name == "posix":
smtio_reclimit = 64 * 1024
- smtio_stacksize = 128 * 1024 * 1024
if sys.getrecursionlimit() < smtio_reclimit:
sys.setrecursionlimit(smtio_reclimit)
- if resource.getrlimit(resource.RLIMIT_STACK)[0] < smtio_stacksize:
- resource.setrlimit(resource.RLIMIT_STACK, (smtio_stacksize, -1))
+
+ current_rlimit_stack = resource.getrlimit(resource.RLIMIT_STACK)
+ if current_rlimit_stack[0] != resource.RLIM_INFINITY:
+ smtio_stacksize = 128 * 1024 * 1024
+ if os.uname().sysname == "Darwin":
+ # MacOS has rather conservative stack limits
+ smtio_stacksize = 16 * 1024 * 1024
+ if current_rlimit_stack[1] != resource.RLIM_INFINITY:
+ smtio_stacksize = min(smtio_stacksize, current_rlimit_stack[1])
+ if current_rlimit_stack[0] < smtio_stacksize:
+ resource.setrlimit(resource.RLIMIT_STACK, (smtio_stacksize, current_rlimit_stack[1]))
# currently running solvers (so we can kill them)
@@ -158,19 +166,28 @@ class SmtIo:
self.unroll = False
if self.solver == "yices":
- self.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts
+ if self.noincr:
+ self.popen_vargs = ['yices-smt2'] + self.solver_opts
+ else:
+ self.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts
if self.solver == "z3":
self.popen_vargs = ['z3', '-smt2', '-in'] + self.solver_opts
if self.solver == "cvc4":
- self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts
+ if self.noincr:
+ self.popen_vargs = ['cvc4', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts
+ else:
+ self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts
if self.solver == "mathsat":
self.popen_vargs = ['mathsat'] + self.solver_opts
if self.solver == "boolector":
- self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts
+ if self.noincr:
+ self.popen_vargs = ['boolector', '--smt2'] + self.solver_opts
+ else:
+ self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts
self.unroll = True
if self.solver == "abc":
@@ -767,7 +784,7 @@ class SmtIo:
def get_path(self, mod, path):
assert mod in self.modinfo
- path = path.split(".")
+ path = path.replace("\\", "/").split(".")
for i in range(len(path)-1):
first = ".".join(path[0:i+1])
diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc
index b8383412..f379c9c4 100644
--- a/backends/smv/smv.cc
+++ b/backends/smv/smv.cc
@@ -42,7 +42,7 @@ struct SmvWorker
pool<Wire*> partial_assignment_wires;
dict<SigBit, std::pair<const char*, int>> partial_assignment_bits;
- vector<string> assignments, invarspecs;
+ vector<string> inputvars, vars, definitions, assignments, invarspecs;
const char *cid()
{
@@ -195,7 +195,7 @@ struct SmvWorker
return rvalue(sig);
const char *temp_id = cid();
- f << stringf(" %s : unsigned word[%d]; -- %s\n", temp_id, GetSize(sig), log_signal(sig));
+// f << stringf(" %s : unsigned word[%d]; -- %s\n", temp_id, GetSize(sig), log_signal(sig));
int offset = 0;
for (auto bit : sig) {
@@ -210,14 +210,14 @@ struct SmvWorker
void run()
{
f << stringf("MODULE %s\n", cid(module->name));
- f << stringf(" VAR\n");
for (auto wire : module->wires())
{
if (SigSpec(wire) != sigmap(wire))
partial_assignment_wires.insert(wire);
- f << stringf(" %s : unsigned word[%d]; -- %s\n", cid(wire->name), wire->width, log_id(wire));
+ if (wire->port_input)
+ inputvars.push_back(stringf("%s : unsigned word[%d]; -- %s", cid(wire->name), wire->width, log_id(wire)));
if (wire->attributes.count("\\init"))
assignments.push_back(stringf("init(%s) := %s;", lvalue(wire), rvalue(wire->attributes.at("\\init"))));
@@ -275,8 +275,8 @@ struct SmvWorker
const char *b_shr = rvalue_u(sig_b);
const char *b_shl = cid();
- f << stringf(" %s : unsigned word[%d]; -- neg(%s)\n", b_shl, GetSize(sig_b), log_signal(sig_b));
- assignments.push_back(stringf("%s := unsigned(-%s);", b_shl, rvalue_s(sig_b)));
+// f << stringf(" %s : unsigned word[%d]; -- neg(%s)\n", b_shl, GetSize(sig_b), log_signal(sig_b));
+ definitions.push_back(stringf("%s := unsigned(-%s);", b_shl, rvalue_s(sig_b)));
string expr_shl = stringf("resize(%s << %s[%d:0], %d)", expr_a.c_str(), b_shl, shift_b_width-1, width_y);
string expr_shr = stringf("resize(%s >> %s[%d:0], %d)", expr_a.c_str(), b_shr, shift_b_width-1, width_y);
@@ -303,7 +303,7 @@ struct SmvWorker
GetSize(sig_b)-shift_b_width, width_y, expr.c_str());
}
- assignments.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str()));
+ definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str()));
continue;
}
@@ -319,12 +319,12 @@ struct SmvWorker
if (cell->getParam("\\A_SIGNED").as_bool())
{
- assignments.push_back(stringf("%s := unsigned(%s%s);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := unsigned(%s%s);", lvalue(cell->getPort("\\Y")),
op.c_str(), rvalue_s(cell->getPort("\\A"), width)));
}
else
{
- assignments.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")),
op.c_str(), rvalue_u(cell->getPort("\\A"), width)));
}
@@ -346,12 +346,12 @@ struct SmvWorker
if (cell->getParam("\\A_SIGNED").as_bool())
{
- assignments.push_back(stringf("%s := unsigned(%s %s %s);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := unsigned(%s %s %s);", lvalue(cell->getPort("\\Y")),
rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width)));
}
else
{
- assignments.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")),
rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width)));
}
@@ -370,12 +370,12 @@ struct SmvWorker
if (cell->getParam("\\A_SIGNED").as_bool())
{
- assignments.push_back(stringf("%s := resize(unsigned(%s %s %s), %d);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := resize(unsigned(%s %s %s), %d);", lvalue(cell->getPort("\\Y")),
rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width), width_y));
}
else
{
- assignments.push_back(stringf("%s := resize(%s %s %s, %d);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := resize(%s %s %s, %d);", lvalue(cell->getPort("\\Y")),
rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width), width_y));
}
@@ -407,7 +407,7 @@ struct SmvWorker
expr_b = stringf("resize(%s, %d)", rvalue(cell->getPort("\\B")), width);
}
- assignments.push_back(stringf("%s := resize(word1(%s %s %s), %d);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := resize(word1(%s %s %s), %d);", lvalue(cell->getPort("\\Y")),
expr_a.c_str(), op.c_str(), expr_b.c_str(), GetSize(cell->getPort("\\Y"))));
continue;
@@ -425,7 +425,7 @@ struct SmvWorker
if (cell->type == "$reduce_or") expr = stringf("%s != 0ub%d_0", expr_a, width_a);
if (cell->type == "$reduce_bool") expr = stringf("%s != 0ub%d_0", expr_a, width_a);
- assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
+ definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
continue;
}
@@ -444,7 +444,7 @@ struct SmvWorker
if (cell->type == "$reduce_xnor")
expr = "!(" + expr + ")";
- assignments.push_back(stringf("%s := resize(%s, %d);", expr_y, expr.c_str(), width_y));
+ definitions.push_back(stringf("%s := resize(%s, %d);", expr_y, expr.c_str(), width_y));
continue;
}
@@ -462,7 +462,7 @@ struct SmvWorker
if (cell->type == "$logic_and") expr = expr_a + " & " + expr_b;
if (cell->type == "$logic_or") expr = expr_a + " | " + expr_b;
- assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
+ definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
continue;
}
@@ -474,7 +474,7 @@ struct SmvWorker
string expr_a = stringf("(%s = 0ub%d_0)", rvalue(cell->getPort("\\A")), width_a);
const char *expr_y = lvalue(cell->getPort("\\Y"));
- assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a.c_str(), width_y));
+ definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a.c_str(), width_y));
continue;
}
@@ -490,12 +490,13 @@ struct SmvWorker
expr += stringf("bool(%s) ? %s : ", rvalue(sig_s[i]), rvalue(sig_b.extract(i*width, width)));
expr += rvalue(sig_a);
- assignments.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str()));
+ definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str()));
continue;
}
if (cell->type == "$dff")
{
+ vars.push_back(stringf("%s : unsigned word[%d]; -- %s", lvalue(cell->getPort("\\Q")), GetSize(cell->getPort("\\Q")), log_signal(cell->getPort("\\Q"))));
assignments.push_back(stringf("next(%s) := %s;", lvalue(cell->getPort("\\Q")), rvalue(cell->getPort("\\D"))));
continue;
}
@@ -503,7 +504,7 @@ struct SmvWorker
if (cell->type.in("$_BUF_", "$_NOT_"))
{
string op = cell->type == "$_NOT_" ? "!" : "";
- assignments.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue(cell->getPort("\\A"))));
+ definitions.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue(cell->getPort("\\A"))));
continue;
}
@@ -517,49 +518,49 @@ struct SmvWorker
if (cell->type.in("$_XNOR_")) op = "xnor";
if (cell->type.in("$_ANDNOT_", "$_ORNOT_"))
- assignments.push_back(stringf("%s := %s %s (!%s);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := %s %s (!%s);", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B"))));
else
if (cell->type.in("$_NAND_", "$_NOR_"))
- assignments.push_back(stringf("%s := !(%s %s %s);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := !(%s %s %s);", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B"))));
else
- assignments.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B"))));
continue;
}
if (cell->type == "$_MUX_")
{
- assignments.push_back(stringf("%s := bool(%s) ? %s : %s;", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := bool(%s) ? %s : %s;", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\S")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\A"))));
continue;
}
if (cell->type == "$_AOI3_")
{
- assignments.push_back(stringf("%s := !((%s & %s) | %s);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := !((%s & %s) | %s);", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C"))));
continue;
}
if (cell->type == "$_OAI3_")
{
- assignments.push_back(stringf("%s := !((%s | %s) & %s);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := !((%s | %s) & %s);", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C"))));
continue;
}
if (cell->type == "$_AOI4_")
{
- assignments.push_back(stringf("%s := !((%s & %s) | (%s & %s));", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := !((%s & %s) | (%s & %s));", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D"))));
continue;
}
if (cell->type == "$_OAI4_")
{
- assignments.push_back(stringf("%s := !((%s | %s) & (%s | %s));", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := !((%s | %s) & (%s | %s));", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D"))));
continue;
}
@@ -567,13 +568,13 @@ struct SmvWorker
if (cell->type[0] == '$')
log_error("Found currently unsupported cell type %s (%s.%s).\n", log_id(cell->type), log_id(module), log_id(cell));
- f << stringf(" %s : %s;\n", cid(cell->name), cid(cell->type));
+// f << stringf(" %s : %s;\n", cid(cell->name), cid(cell->type));
for (auto &conn : cell->connections())
if (cell->output(conn.first))
- assignments.push_back(stringf("%s := %s.%s;", lvalue(conn.second), cid(cell->name), cid(conn.first)));
+ definitions.push_back(stringf("%s := %s.%s;", lvalue(conn.second), cid(cell->name), cid(conn.first)));
else
- assignments.push_back(stringf("%s.%s := %s;", cid(cell->name), cid(conn.first), rvalue(conn.second)));
+ definitions.push_back(stringf("%s.%s := %s;", cid(cell->name), cid(conn.first), rvalue(conn.second)));
}
for (Wire *wire : partial_assignment_wires)
@@ -657,7 +658,25 @@ struct SmvWorker
}
}
- assignments.push_back(stringf("%s := %s;", cid(wire->name), expr.c_str()));
+ definitions.push_back(stringf("%s := %s;", cid(wire->name), expr.c_str()));
+ }
+
+ if (!inputvars.empty()) {
+ f << stringf(" IVAR\n");
+ for (const string &line : inputvars)
+ f << stringf(" %s\n", line.c_str());
+ }
+
+ if (!vars.empty()) {
+ f << stringf(" VAR\n");
+ for (const string &line : vars)
+ f << stringf(" %s\n", line.c_str());
+ }
+
+ if (!definitions.empty()) {
+ f << stringf(" DEFINE\n");
+ for (const string &line : definitions)
+ f << stringf(" %s\n", line.c_str());
}
if (!assignments.empty()) {
diff --git a/backends/table/table.cc b/backends/table/table.cc
index 979273dd..b75169ea 100644
--- a/backends/table/table.cc
+++ b/backends/table/table.cc
@@ -109,7 +109,7 @@ struct TableBackend : public Backend {
else if (cell->output(conn.first))
*f << "out" << "\t";
else
- *f << "unkown" << "\t";
+ *f << "unknown" << "\t";
*f << log_signal(sigmap(conn.second)) << "\n";
}
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc
index ae903151..83d83f48 100644
--- a/backends/verilog/verilog_backend.cc
+++ b/backends/verilog/verilog_backend.cc
@@ -33,7 +33,7 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, defparam, decimal;
+bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, defparam, decimal, siminit;
int auto_name_counter, auto_name_offset, auto_name_digits;
std::map<RTLIL::IdString, int> auto_name_map;
std::set<RTLIL::IdString> reg_wires, reg_ct;
@@ -126,6 +126,33 @@ std::string id(RTLIL::IdString internal_id, bool may_rename = true)
break;
}
+ const pool<string> keywords = {
+ // IEEE 1800-2017 Annex B
+ "accept_on", "alias", "always", "always_comb", "always_ff", "always_latch", "and", "assert", "assign", "assume", "automatic", "before",
+ "begin", "bind", "bins", "binsof", "bit", "break", "buf", "bufif0", "bufif1", "byte", "case", "casex", "casez", "cell", "chandle",
+ "checker", "class", "clocking", "cmos", "config", "const", "constraint", "context", "continue", "cover", "covergroup", "coverpoint",
+ "cross", "deassign", "default", "defparam", "design", "disable", "dist", "do", "edge", "else", "end", "endcase", "endchecker",
+ "endclass", "endclocking", "endconfig", "endfunction", "endgenerate", "endgroup", "endinterface", "endmodule", "endpackage",
+ "endprimitive", "endprogram", "endproperty", "endsequence", "endspecify", "endtable", "endtask", "enum", "event", "eventually",
+ "expect", "export", "extends", "extern", "final", "first_match", "for", "force", "foreach", "forever", "fork", "forkjoin", "function",
+ "generate", "genvar", "global", "highz0", "highz1", "if", "iff", "ifnone", "ignore_bins", "illegal_bins", "implements", "implies",
+ "import", "incdir", "include", "initial", "inout", "input", "inside", "instance", "int", "integer", "interconnect", "interface",
+ "intersect", "join", "join_any", "join_none", "large", "let", "liblist", "library", "local", "localparam", "logic", "longint",
+ "macromodule", "matches", "medium", "modport", "module", "nand", "negedge", "nettype", "new", "nexttime", "nmos", "nor",
+ "noshowcancelled", "not", "notif0", "notif1", "null", "or", "output", "package", "packed", "parameter", "pmos", "posedge", "primitive",
+ "priority", "program", "property", "protected", "pull0", "pull1", "pulldown", "pullup", "pulsestyle_ondetect", "pulsestyle_onevent",
+ "pure", "rand", "randc", "randcase", "randsequence", "rcmos", "real", "realtime", "ref", "reg", "reject_on", "release", "repeat",
+ "restrict", "return", "rnmos", "rpmos", "rtran", "rtranif0", "rtranif1", "s_always", "s_eventually", "s_nexttime", "s_until",
+ "s_until_with", "scalared", "sequence", "shortint", "shortreal", "showcancelled", "signed", "small", "soft", "solve", "specify",
+ "specparam", "static", "string", "strong", "strong0", "strong1", "struct", "super", "supply0", "supply1", "sync_accept_on",
+ "sync_reject_on", "table", "tagged", "task", "this", "throughout", "time", "timeprecision", "timeunit", "tran", "tranif0", "tranif1",
+ "tri", "tri0", "tri1", "triand", "trior", "trireg", "type", "typedef", "union", "unique", "unique0", "unsigned", "until", "until_with",
+ "untyped", "use", "uwire", "var", "vectored", "virtual", "void", "wait", "wait_order", "wand", "weak", "weak0", "weak1", "while",
+ "wildcard", "wire", "with", "within", "wor", "xnor", "xor",
+ };
+ if (keywords.count(str))
+ do_escape = true;
+
if (do_escape)
return "\\" + std::string(str) + " ";
return std::string(str);
@@ -388,7 +415,7 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory)
{
dump_attributes(f, indent, memory->attributes);
- f << stringf("%s" "reg [%d:0] %s [%d:0];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size-1);
+ f << stringf("%s" "reg [%d:0] %s [%d:%d];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size+memory->start_offset-1, memory->start_offset);
}
void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true)
@@ -678,13 +705,45 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
#undef HANDLE_UNIOP
#undef HANDLE_BINOP
- if (cell->type == "$shiftx")
+ if (cell->type == "$shift")
{
f << stringf("%s" "assign ", indent.c_str());
dump_sigspec(f, cell->getPort("\\Y"));
f << stringf(" = ");
+ if (cell->getParam("\\B_SIGNED").as_bool())
+ {
+ f << stringf("$signed(");
+ dump_sigspec(f, cell->getPort("\\B"));
+ f << stringf(")");
+ f << stringf(" < 0 ? ");
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf(" << - ");
+ dump_sigspec(f, cell->getPort("\\B"));
+ f << stringf(" : ");
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf(" >> ");
+ dump_sigspec(f, cell->getPort("\\B"));
+ }
+ else
+ {
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf(" >> ");
+ dump_sigspec(f, cell->getPort("\\B"));
+ }
+ f << stringf(";\n");
+ return true;
+ }
+
+ if (cell->type == "$shiftx")
+ {
+ std::string temp_id = next_auto_id();
+ f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort("\\A"))-1, temp_id.c_str());
dump_sigspec(f, cell->getPort("\\A"));
- f << stringf("[");
+ f << stringf(";\n");
+
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = %s[", temp_id.c_str());
if (cell->getParam("\\B_SIGNED").as_bool())
f << stringf("$signed(");
dump_sigspec(f, cell->getPort("\\B"));
@@ -757,6 +816,18 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
+ if (cell->type == "$tribuf")
+ {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = ");
+ dump_sigspec(f, cell->getPort("\\EN"));
+ f << stringf(" ? ");
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf(" : %d'bz;\n", cell->parameters.at("\\WIDTH").as_int());
+ return true;
+ }
+
if (cell->type == "$slice")
{
f << stringf("%s" "assign ", indent.c_str());
@@ -952,6 +1023,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
std::string mem_id = id(cell->parameters["\\MEMID"].decode_string());
int abits = cell->parameters["\\ABITS"].as_int();
int size = cell->parameters["\\SIZE"].as_int();
+ int offset = cell->parameters["\\OFFSET"].as_int();
int width = cell->parameters["\\WIDTH"].as_int();
bool use_init = !(RTLIL::SigSpec(cell->parameters["\\INIT"]).is_fully_undef());
@@ -960,7 +1032,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
// initial begin
// memid[0] = ...
// end
- f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size-1, 0);
+ f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size+offset-1, offset);
if (use_init)
{
f << stringf("%s" "initial begin\n", indent.c_str());
@@ -993,43 +1065,46 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
use_rd_clk = cell->parameters["\\RD_CLK_ENABLE"].extract(i).as_bool();
rd_clk_posedge = cell->parameters["\\RD_CLK_POLARITY"].extract(i).as_bool();
rd_transparent = cell->parameters["\\RD_TRANSPARENT"].extract(i).as_bool();
+ if (use_rd_clk)
{
- std::ostringstream os;
- dump_sigspec(os, sig_rd_clk);
- clk_domain_str = stringf("%sedge %s", rd_clk_posedge ? "pos" : "neg", os.str().c_str());
- if( clk_to_lof_body.count(clk_domain_str) == 0 )
- clk_to_lof_body[clk_domain_str] = std::vector<std::string>();
- }
- if (use_rd_clk && !rd_transparent)
- {
- // for clocked read ports make something like:
- // reg [..] temp_id;
- // always @(posedge clk)
- // if (rd_en) temp_id <= array_reg[r_addr];
- // assign r_data = temp_id;
- std::string temp_id = next_auto_id();
- lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_data.size() - 1, temp_id.c_str()) );
{
std::ostringstream os;
- if (sig_rd_en != RTLIL::SigBit(true))
+ dump_sigspec(os, sig_rd_clk);
+ clk_domain_str = stringf("%sedge %s", rd_clk_posedge ? "pos" : "neg", os.str().c_str());
+ if( clk_to_lof_body.count(clk_domain_str) == 0 )
+ clk_to_lof_body[clk_domain_str] = std::vector<std::string>();
+ }
+ if (!rd_transparent)
+ {
+ // for clocked read ports make something like:
+ // reg [..] temp_id;
+ // always @(posedge clk)
+ // if (rd_en) temp_id <= array_reg[r_addr];
+ // assign r_data = temp_id;
+ std::string temp_id = next_auto_id();
+ lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_data.size() - 1, temp_id.c_str()) );
{
- os << stringf("if (");
- dump_sigspec(os, sig_rd_en);
- os << stringf(") ");
+ std::ostringstream os;
+ if (sig_rd_en != RTLIL::SigBit(true))
+ {
+ os << stringf("if (");
+ dump_sigspec(os, sig_rd_en);
+ os << stringf(") ");
+ }
+ os << stringf("%s <= %s[", temp_id.c_str(), mem_id.c_str());
+ dump_sigspec(os, sig_rd_addr);
+ os << stringf("];\n");
+ clk_to_lof_body[clk_domain_str].push_back(os.str());
+ }
+ {
+ std::ostringstream os;
+ dump_sigspec(os, sig_rd_data);
+ std::string line = stringf("assign %s = %s;\n", os.str().c_str(), temp_id.c_str());
+ clk_to_lof_body[""].push_back(line);
}
- os << stringf("%s <= %s[", temp_id.c_str(), mem_id.c_str());
- dump_sigspec(os, sig_rd_addr);
- os << stringf("];\n");
- clk_to_lof_body[clk_domain_str].push_back(os.str());
}
+ else
{
- std::ostringstream os;
- dump_sigspec(os, sig_rd_data);
- std::string line = stringf("assign %s = %s;\n", os.str().c_str(), temp_id.c_str());
- clk_to_lof_body[""].push_back(line);
- }
- } else {
- if (rd_transparent) {
// for rd-transparent read-ports make something like:
// reg [..] temp_id;
// always @(posedge clk)
@@ -1049,15 +1124,15 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), temp_id.c_str());
clk_to_lof_body[""].push_back(line);
}
- } else {
- // for non-clocked read-ports make something like:
- // assign r_data = array_reg[r_addr];
- std::ostringstream os, os2;
- dump_sigspec(os, sig_rd_data);
- dump_sigspec(os2, sig_rd_addr);
- std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str());
- clk_to_lof_body[""].push_back(line);
}
+ } else {
+ // for non-clocked read-ports make something like:
+ // assign r_data = array_reg[r_addr];
+ std::ostringstream os, os2;
+ dump_sigspec(os, sig_rd_data);
+ dump_sigspec(os2, sig_rd_addr);
+ std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str());
+ clk_to_lof_body[""].push_back(line);
}
}
@@ -1235,6 +1310,15 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
}
}
+ if (siminit && reg_ct.count(cell->type) && cell->hasPort("\\Q")) {
+ std::stringstream ss;
+ dump_reg_init(ss, cell->getPort("\\Q"));
+ if (!ss.str().empty()) {
+ f << stringf("%sinitial %s.Q", indent.c_str(), cell_name.c_str());
+ f << ss.str();
+ f << ";\n";
+ }
+ }
}
void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
@@ -1351,6 +1435,8 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo
if (sync->type == RTLIL::STa) {
f << stringf("%s" "always @* begin\n", indent.c_str());
+ } else if (sync->type == RTLIL::STi) {
+ f << stringf("%s" "initial begin\n", indent.c_str());
} else {
f << stringf("%s" "always @(", indent.c_str());
if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1)
@@ -1415,10 +1501,10 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
}
if (!module->processes.empty())
- log_warning("Module %s contains unmapped RTLIL proccesses. RTLIL processes\n"
+ log_warning("Module %s contains unmapped RTLIL processes. RTLIL processes\n"
"can't always be mapped directly to Verilog always blocks. Unintended\n"
"changes in simulation behavior are possible! Use \"proc\" to convert\n"
- "processes to logic networks and registers.", log_id(module));
+ "processes to logic networks and registers.\n", log_id(module));
f << stringf("\n");
for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
@@ -1521,6 +1607,10 @@ struct VerilogBackend : public Backend {
log(" without this option all internal cells are converted to Verilog\n");
log(" expressions.\n");
log("\n");
+ log(" -siminit\n");
+ log(" add initial statements with hierarchical refs to initialize FFs when\n");
+ log(" in -noexpr mode.\n");
+ log("\n");
log(" -nodec\n");
log(" 32-bit constant values are by default dumped as decimal numbers,\n");
log(" not bit pattern. This option deactivates this feature and instead\n");
@@ -1577,11 +1667,14 @@ struct VerilogBackend : public Backend {
nostr = false;
defparam = false;
decimal = false;
+ siminit = false;
auto_prefix = "";
bool blackboxes = false;
bool selected = false;
+ auto_name_map.clear();
+ reg_wires.clear();
reg_ct.clear();
reg_ct.insert("$dff");
@@ -1653,6 +1746,10 @@ struct VerilogBackend : public Backend {
decimal = true;
continue;
}
+ if (arg == "-siminit") {
+ siminit = true;
+ continue;
+ }
if (arg == "-blackboxes") {
blackboxes = true;
continue;
@@ -1684,6 +1781,8 @@ struct VerilogBackend : public Backend {
dump_module(*f, "", it->second);
}
+ auto_name_map.clear();
+ reg_wires.clear();
reg_ct.clear();
}
} VerilogBackend;
diff --git a/examples/anlogic/.gitignore b/examples/anlogic/.gitignore
new file mode 100644
index 00000000..97c978a1
--- /dev/null
+++ b/examples/anlogic/.gitignore
@@ -0,0 +1,7 @@
+demo.bit
+demo_phy.area
+full.v
+*.log
+*.h
+*.tde
+*.svf
diff --git a/examples/anlogic/README b/examples/anlogic/README
new file mode 100644
index 00000000..35d8e9cb
--- /dev/null
+++ b/examples/anlogic/README
@@ -0,0 +1,12 @@
+LED Blink project for Anlogic Lichee Tang board.
+
+Follow the install instructions for the Tang Dynasty IDE from given link below.
+
+https://tang.sipeed.com/en/getting-started/installing-td-ide/linux/
+
+
+set TD_HOME env variable to the full path to the TD <TD Install Directory> as follow.
+
+export TD_HOME=<TD Install Directory>
+
+then run "bash build.sh" in this directory.
diff --git a/examples/anlogic/build.sh b/examples/anlogic/build.sh
new file mode 100755
index 00000000..e0f6b4cf
--- /dev/null
+++ b/examples/anlogic/build.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -ex
+yosys demo.ys
+$TD_HOME/bin/td build.tcl
diff --git a/examples/anlogic/build.tcl b/examples/anlogic/build.tcl
new file mode 100644
index 00000000..06db525c
--- /dev/null
+++ b/examples/anlogic/build.tcl
@@ -0,0 +1,11 @@
+import_device eagle_s20.db -package BG256
+read_verilog full.v -top demo
+read_adc demo.adc
+optimize_rtl
+map_macro
+map
+pack
+place
+route
+report_area -io_info -file demo_phy.area
+bitgen -bit demo.bit -version 0X0000 -svf demo.svf -svf_comment_on -g ucode:00000000000000000000000000000000
diff --git a/examples/anlogic/demo.adc b/examples/anlogic/demo.adc
new file mode 100644
index 00000000..ec802502
--- /dev/null
+++ b/examples/anlogic/demo.adc
@@ -0,0 +1,2 @@
+set_pin_assignment {CLK_IN} { LOCATION = K14; } ##24MHZ
+set_pin_assignment {R_LED} { LOCATION = R3; } ##R_LED
diff --git a/examples/anlogic/demo.v b/examples/anlogic/demo.v
new file mode 100644
index 00000000..e17db771
--- /dev/null
+++ b/examples/anlogic/demo.v
@@ -0,0 +1,18 @@
+module demo (
+ input wire CLK_IN,
+ output wire R_LED
+);
+ parameter time1 = 30'd12_000_000;
+ reg led_state;
+ reg [29:0] count;
+
+ always @(posedge CLK_IN)begin
+ if(count == time1)begin
+ count<= 30'd0;
+ led_state <= ~led_state;
+ end
+ else
+ count <= count + 1'b1;
+ end
+ assign R_LED = led_state;
+endmodule
diff --git a/examples/anlogic/demo.ys b/examples/anlogic/demo.ys
new file mode 100644
index 00000000..cb396cc2
--- /dev/null
+++ b/examples/anlogic/demo.ys
@@ -0,0 +1,3 @@
+read_verilog demo.v
+synth_anlogic -top demo
+write_verilog full.v
diff --git a/examples/igloo2/.gitignore b/examples/igloo2/.gitignore
new file mode 100644
index 00000000..33b7182d
--- /dev/null
+++ b/examples/igloo2/.gitignore
@@ -0,0 +1,4 @@
+/netlist.edn
+/netlist.vm
+/example.stp
+/proj
diff --git a/examples/igloo2/example.pdc b/examples/igloo2/example.pdc
new file mode 100644
index 00000000..298d9e93
--- /dev/null
+++ b/examples/igloo2/example.pdc
@@ -0,0 +1,20 @@
+# Add placement constraints here
+
+set_io clk -pinname H16 -fixed yes -DIRECTION INPUT
+
+set_io SW1 -pinname H12 -fixed yes -DIRECTION INPUT
+set_io SW2 -pinname H13 -fixed yes -DIRECTION INPUT
+
+set_io LED1 -pinname J16 -fixed yes -DIRECTION OUTPUT
+set_io LED2 -pinname M16 -fixed yes -DIRECTION OUTPUT
+set_io LED3 -pinname K16 -fixed yes -DIRECTION OUTPUT
+set_io LED4 -pinname N16 -fixed yes -DIRECTION OUTPUT
+
+set_io AA -pinname L12 -fixed yes -DIRECTION OUTPUT
+set_io AB -pinname L13 -fixed yes -DIRECTION OUTPUT
+set_io AC -pinname M13 -fixed yes -DIRECTION OUTPUT
+set_io AD -pinname N15 -fixed yes -DIRECTION OUTPUT
+set_io AE -pinname L11 -fixed yes -DIRECTION OUTPUT
+set_io AF -pinname L14 -fixed yes -DIRECTION OUTPUT
+set_io AG -pinname N14 -fixed yes -DIRECTION OUTPUT
+set_io CA -pinname M15 -fixed yes -DIRECTION OUTPUT
diff --git a/examples/igloo2/example.sdc b/examples/igloo2/example.sdc
new file mode 100644
index 00000000..f8b48731
--- /dev/null
+++ b/examples/igloo2/example.sdc
@@ -0,0 +1,2 @@
+# Add timing constraints here
+create_clock -period 10.000 -waveform {0.000 5.000} [get_ports {clk}]
diff --git a/examples/igloo2/example.v b/examples/igloo2/example.v
new file mode 100644
index 00000000..4a9486e5
--- /dev/null
+++ b/examples/igloo2/example.v
@@ -0,0 +1,64 @@
+module example (
+ input clk,
+ input SW1,
+ input SW2,
+ output LED1,
+ output LED2,
+ output LED3,
+ output LED4,
+
+ output AA, AB, AC, AD,
+ output AE, AF, AG, CA
+);
+
+ localparam BITS = 8;
+ localparam LOG2DELAY = 22;
+
+ reg [BITS+LOG2DELAY-1:0] counter = 0;
+ reg [BITS-1:0] outcnt;
+
+ always @(posedge clk) begin
+ counter <= counter + SW1 + SW2 + 1;
+ outcnt <= counter >> LOG2DELAY;
+ end
+
+ assign {LED1, LED2, LED3, LED4} = outcnt ^ (outcnt >> 1);
+
+ // assign CA = counter[10];
+ // seg7enc seg7encinst (
+ // .seg({AA, AB, AC, AD, AE, AF, AG}),
+ // .dat(CA ? outcnt[3:0] : outcnt[7:4])
+ // );
+
+ assign {AA, AB, AC, AD, AE, AF, AG} = ~(7'b 100_0000 >> outcnt[6:4]);
+ assign CA = outcnt[7];
+endmodule
+
+module seg7enc (
+ input [3:0] dat,
+ output [6:0] seg
+);
+ reg [6:0] seg_inv;
+ always @* begin
+ seg_inv = 0;
+ case (dat)
+ 4'h0: seg_inv = 7'b 0111111;
+ 4'h1: seg_inv = 7'b 0000110;
+ 4'h2: seg_inv = 7'b 1011011;
+ 4'h3: seg_inv = 7'b 1001111;
+ 4'h4: seg_inv = 7'b 1100110;
+ 4'h5: seg_inv = 7'b 1101101;
+ 4'h6: seg_inv = 7'b 1111101;
+ 4'h7: seg_inv = 7'b 0000111;
+ 4'h8: seg_inv = 7'b 1111111;
+ 4'h9: seg_inv = 7'b 1101111;
+ 4'hA: seg_inv = 7'b 1110111;
+ 4'hB: seg_inv = 7'b 1111100;
+ 4'hC: seg_inv = 7'b 0111001;
+ 4'hD: seg_inv = 7'b 1011110;
+ 4'hE: seg_inv = 7'b 1111001;
+ 4'hF: seg_inv = 7'b 1110001;
+ endcase
+ end
+ assign seg = ~seg_inv;
+endmodule
diff --git a/examples/igloo2/libero.tcl b/examples/igloo2/libero.tcl
new file mode 100644
index 00000000..abc94e47
--- /dev/null
+++ b/examples/igloo2/libero.tcl
@@ -0,0 +1,57 @@
+# Run with "libero SCRIPT:libero.tcl"
+
+file delete -force proj
+
+new_project \
+ -name example \
+ -location proj \
+ -block_mode 0 \
+ -hdl "VERILOG" \
+ -family IGLOO2 \
+ -die PA4MGL2500 \
+ -package vf256 \
+ -speed -1
+
+import_files -hdl_source {netlist.vm}
+import_files -sdc {example.sdc}
+import_files -io_pdc {example.pdc}
+build_design_hierarchy
+set_option -synth 0
+
+organize_tool_files -tool PLACEROUTE \
+ -file {proj/constraint/example.sdc} \
+ -file {proj/constraint/io/example.pdc} \
+ -input_type constraint
+
+organize_tool_files -tool VERIFYTIMING \
+ -file {proj/constraint/example.sdc} \
+ -input_type constraint
+
+configure_tool -name PLACEROUTE \
+ -params TDPR:true \
+ -params PDPR:false \
+ -params EFFORT_LEVEL:false \
+ -params REPAIR_MIN_DELAY:false
+
+puts ""
+puts "**> COMPILE"
+run_tool -name {COMPILE}
+puts "<** COMPILE"
+
+puts ""
+puts "**> PLACEROUTE"
+run_tool -name {PLACEROUTE}
+puts "<** PLACEROUTE"
+
+puts ""
+puts "**> VERIFYTIMING"
+run_tool -name {VERIFYTIMING}
+puts "<** VERIFYTIMING"
+
+puts ""
+puts "**> BITSTREAM"
+export_bitstream_file -trusted_facility_file 1 -trusted_facility_file_components {FABRIC}
+puts "<** BITSTREAM"
+
+puts ""
+exit 0
diff --git a/examples/igloo2/runme.sh b/examples/igloo2/runme.sh
new file mode 100644
index 00000000..a08894e0
--- /dev/null
+++ b/examples/igloo2/runme.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -ex
+yosys -p 'synth_sf2 -top example -edif netlist.edn -vlog netlist.vm' example.v
+export LM_LICENSE_FILE=${LM_LICENSE_FILE:-1702@localhost}
+/opt/microsemi/Libero_SoC_v12.0/Libero/bin/libero SCRIPT:libero.tcl
+cp proj/designer/example/export/example.stp .
diff --git a/frontends/aiger/Makefile.inc b/frontends/aiger/Makefile.inc
new file mode 100644
index 00000000..bc111245
--- /dev/null
+++ b/frontends/aiger/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += frontends/aiger/aigerparse.o
+
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc
new file mode 100644
index 00000000..cf7950c8
--- /dev/null
+++ b/frontends/aiger/aigerparse.cc
@@ -0,0 +1,414 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 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.
+ *
+ */
+
+// [[CITE]] The AIGER And-Inverter Graph (AIG) Format Version 20071012
+// Armin Biere. The AIGER And-Inverter Graph (AIG) Format Version 20071012. Technical Report 07/1, October 2011, FMV Reports Series, Institute for Formal Models and Verification, Johannes Kepler University, Altenbergerstr. 69, 4040 Linz, Austria.
+// http://fmv.jku.at/papers/Biere-FMV-TR-07-1.pdf
+
+#ifndef _WIN32
+#include <libgen.h>
+#endif
+#include <array>
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+#include "aigerparse.h"
+
+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)
+{
+ 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);
+}
+
+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;
+}
+
+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
+}
+
+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)));
+}
+
+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);
+ }
+}
+
+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>)"
+#ifdef _WIN32
+ "top" // FIXME
+#else
+ "<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;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-module_name" && argidx+1 < args.size()) {
+ module_name = RTLIL::escape_id(args[++argidx]);
+ continue;
+ }
+ if (arg == "-clk_name" && argidx+1 < args.size()) {
+ clk_name = RTLIL::escape_id(args[++argidx]);
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ if (module_name.empty()) {
+#ifdef _WIN32
+ module_name = "top"; // FIXME: basename equivalent on Win32?
+#else
+ char* bn = strdup(filename.c_str());
+ module_name = RTLIL::escape_id(bn);
+ free(bn);
+#endif
+ }
+
+ 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
new file mode 100644
index 00000000..c49cd152
--- /dev/null
+++ b/frontends/aiger/aigerparse.h
@@ -0,0 +1,51 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 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.
+ *
+ */
+
+#ifndef ABC_AIGERPARSE
+#define ABC_AIGERPARSE
+
+#include "kernel/yosys.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct AigerReader
+{
+ RTLIL::Design *design;
+ std::istream &f;
+ RTLIL::IdString clk_name;
+ RTLIL::Module *module;
+
+ unsigned M, I, L, O, A;
+ unsigned B, C, J, F; // Optional in AIGER 1.9
+ unsigned line_count;
+
+ std::vector<RTLIL::Wire*> inputs;
+ std::vector<RTLIL::Wire*> latches;
+ std::vector<RTLIL::Wire*> outputs;
+
+ AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name);
+ void parse_aiger();
+ void parse_aiger_ascii();
+ void parse_aiger_binary();
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index e79be953..d4899616 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -2,6 +2,7 @@
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -35,16 +36,16 @@ YOSYS_NAMESPACE_BEGIN
using namespace AST;
using namespace AST_INTERNAL;
-// instanciate global variables (public API)
+// instantiate global variables (public API)
namespace AST {
std::string current_filename;
void (*set_line_num)(int) = NULL;
int (*get_line_num)() = NULL;
}
-// instanciate global variables (private API)
+// instantiate global variables (private API)
namespace AST_INTERNAL {
- bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit;
+ 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;
AstNode *current_ast, *current_ast_mod;
std::map<std::string, AstNode*> current_scope;
@@ -171,8 +172,7 @@ bool AstNode::get_bool_attribute(RTLIL::IdString id)
AstNode *attr = attributes.at(id);
if (attr->type != AST_CONSTANT)
- log_file_error(attr->filename, attr->linenum, "Attribute `%s' with non-constant value!\n",
- id.c_str());
+ log_file_error(attr->filename, attr->linenum, "Attribute `%s' with non-constant value!\n", id.c_str());
return attr->integer != 0;
}
@@ -431,9 +431,12 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
break;
case AST_RANGE:
- if (range_valid)
- fprintf(f, "[%d:%d]", range_left, range_right);
- else {
+ if (range_valid) {
+ if (range_swapped)
+ fprintf(f, "[%d:%d]", range_right, range_left);
+ else
+ fprintf(f, "[%d:%d]", range_left, range_right);
+ } else {
for (auto child : children) {
fprintf(f, "%c", first ? '[' : ':');
child->dumpVlog(f, "");
@@ -562,7 +565,8 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
case AST_CONCAT:
fprintf(f, "{");
- for (auto child : children) {
+ for (int i = GetSize(children)-1; i >= 0; i--) {
+ auto child = children[i];
if (!first)
fprintf(f, ", ");
child->dumpVlog(f, "");
@@ -903,9 +907,9 @@ RTLIL::Const AstNode::realAsConst(int width)
}
// create a new AstModule from an AST_MODULE AST node
-static AstModule* process_module(AstNode *ast, bool defer)
+static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast = NULL)
{
- log_assert(ast->type == AST_MODULE);
+ log_assert(ast->type == AST_MODULE || ast->type == AST_INTERFACE);
if (defer)
log("Storing AST representation for module `%s'.\n", ast->str.c_str());
@@ -916,28 +920,38 @@ static AstModule* process_module(AstNode *ast, bool defer)
current_module->ast = NULL;
current_module->name = ast->str;
current_module->attributes["\\src"] = stringf("%s:%d", ast->filename.c_str(), ast->linenum);
+ current_module->set_bool_attribute("\\cells_not_processed");
current_ast_mod = ast;
- AstNode *ast_before_simplify = ast->clone();
+ AstNode *ast_before_simplify;
+ if (original_ast != NULL)
+ ast_before_simplify = original_ast;
+ else
+ ast_before_simplify = ast->clone();
if (flag_dump_ast1) {
- log("Dumping Verilog AST before simplification:\n");
+ log("Dumping AST before simplification:\n");
ast->dumpAst(NULL, " ");
log("--- END OF AST DUMP ---\n");
}
+ if (flag_dump_vlog1) {
+ log("Dumping Verilog AST before simplification:\n");
+ ast->dumpVlog(NULL, " ");
+ log("--- END OF AST DUMP ---\n");
+ }
if (!defer)
{
while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { }
if (flag_dump_ast2) {
- log("Dumping Verilog AST after simplification:\n");
+ log("Dumping AST after simplification:\n");
ast->dumpAst(NULL, " ");
log("--- END OF AST DUMP ---\n");
}
- if (flag_dump_vlog) {
- log("Dumping Verilog AST (as requested by dump_vlog option):\n");
+ if (flag_dump_vlog2) {
+ log("Dumping Verilog AST after simplification:\n");
ast->dumpVlog(NULL, " ");
log("--- END OF AST DUMP ---\n");
}
@@ -963,8 +977,7 @@ static AstModule* process_module(AstNode *ast, bool defer)
for (auto &attr : ast->attributes) {
if (attr.second->type != AST_CONSTANT)
- log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n",
- attr.first.c_str());
+ log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
current_module->attributes[attr.first] = attr.second->asAttrConst();
}
for (size_t i = 0; i < ast->children.size(); i++) {
@@ -989,6 +1002,8 @@ static AstModule* process_module(AstNode *ast, bool defer)
ignoreThisSignalsInInitial = RTLIL::SigSpec();
}
+ if (ast->type == AST_INTERFACE)
+ current_module->set_bool_attribute("\\is_interface");
current_module->ast = ast_before_simplify;
current_module->nolatches = flag_nolatches;
current_module->nomeminit = flag_nomeminit;
@@ -1010,14 +1025,15 @@ static AstModule* process_module(AstNode *ast, bool defer)
}
// create AstModule instances for all modules in the AST tree and add them to 'design'
-void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog, bool dump_rtlil,
+void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_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)
{
current_ast = ast;
flag_dump_ast1 = dump_ast1;
flag_dump_ast2 = dump_ast2;
flag_no_dump_ptr = no_dump_ptr;
- flag_dump_vlog = dump_vlog;
+ flag_dump_vlog1 = dump_vlog1;
+ flag_dump_vlog2 = dump_vlog2;
flag_dump_rtlil = dump_rtlil;
flag_nolatches = nolatches;
flag_nomeminit = nomeminit;
@@ -1031,7 +1047,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
log_assert(current_ast->type == AST_DESIGN);
for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++)
{
- if ((*it)->type == AST_MODULE)
+ if ((*it)->type == AST_MODULE || (*it)->type == AST_INTERFACE)
{
for (auto n : design->verilog_globals)
(*it)->children.push_back(n->clone());
@@ -1053,8 +1069,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump
if (design->has((*it)->str)) {
RTLIL::Module *existing_mod = design->module((*it)->str);
if (!nooverwrite && !overwrite && !existing_mod->get_bool_attribute("\\blackbox")) {
- log_file_error((*it)->filename, (*it)->linenum, "Re-definition of module `%s'!\n",
- (*it)->str.c_str());
+ log_file_error((*it)->filename, (*it)->linenum, "Re-definition of module `%s'!\n", (*it)->str.c_str());
} else if (nooverwrite) {
log("Ignoring re-definition of module `%s' at %s:%d.\n",
(*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum);
@@ -1083,8 +1098,264 @@ AstModule::~AstModule()
delete ast;
}
+
+// An interface port with modport is specified like this:
+// <interface_name>.<modport_name>
+// This function splits the interface_name from the modport_name, and fails if it is not a valid combination
+std::pair<std::string,std::string> AST::split_modport_from_type(std::string name_type)
+{
+ std::string interface_type = "";
+ std::string interface_modport = "";
+ size_t ndots = std::count(name_type.begin(), name_type.end(), '.');
+ // Separate the interface instance name from any modports:
+ if (ndots == 0) { // Does not have modport
+ interface_type = name_type;
+ }
+ else {
+ std::stringstream name_type_stream(name_type);
+ std::string segment;
+ std::vector<std::string> seglist;
+ while(std::getline(name_type_stream, segment, '.')) {
+ seglist.push_back(segment);
+ }
+ if (ndots == 1) { // Has modport
+ interface_type = seglist[0];
+ interface_modport = seglist[1];
+ }
+ else { // Erroneous port type
+ log_error("More than two '.' in signal port type (%s)\n", name_type.c_str());
+ }
+ }
+ return std::pair<std::string,std::string>(interface_type, interface_modport);
+
+}
+
+AstNode * AST::find_modport(AstNode *intf, std::string name)
+{
+ for (auto &ch : intf->children)
+ if (ch->type == AST_MODPORT)
+ if (ch->str == name) // Modport found
+ return ch;
+ return NULL;
+}
+
+// Iterate over all wires in an interface and add them as wires in the AST module:
+void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule, std::string intfname, AstNode *modport)
+{
+ for (auto &wire_it : intfmodule->wires_){
+ AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true)));
+ std::string origname = log_id(wire_it.first);
+ std::string newname = intfname + "." + origname;
+ wire->str = newname;
+ if (modport != NULL) {
+ bool found_in_modport = false;
+ // Search for the current wire in the wire list for the current modport
+ for (auto &ch : modport->children) {
+ if (ch->type == AST_MODPORTMEMBER) {
+ std::string compare_name = "\\" + origname;
+ if (ch->str == compare_name) { // Found signal. The modport decides whether it is input or output
+ found_in_modport = true;
+ wire->is_input = ch->is_input;
+ wire->is_output = ch->is_output;
+ break;
+ }
+ }
+ }
+ if (found_in_modport) {
+ module_ast->children.push_back(wire);
+ }
+ else { // If not found in modport, do not create port
+ delete wire;
+ }
+ }
+ else { // If no modport, set inout
+ wire->is_input = true;
+ wire->is_output = true;
+ module_ast->children.push_back(wire);
+ }
+ }
+}
+
+// When an interface instance is found in a module, the whole RTLIL for the module will be rederived again
+// from AST. The interface members are copied into the AST module with the prefix of the interface.
+void AstModule::reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module*> local_interfaces)
+{
+ bool is_top = false;
+ AstNode *new_ast = ast->clone();
+ for (auto &intf : local_interfaces) {
+ std::string intfname = intf.first.str();
+ RTLIL::Module *intfmodule = intf.second;
+ for (auto &wire_it : intfmodule->wires_){
+ AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true)));
+ std::string newname = log_id(wire_it.first);
+ newname = intfname + "." + newname;
+ wire->str = newname;
+ new_ast->children.push_back(wire);
+ }
+ }
+
+ AstNode *ast_before_replacing_interface_ports = new_ast->clone();
+
+ // Explode all interface ports. Note this will only have an effect on 'top
+ // level' modules. Other sub-modules will have their interface ports
+ // exploded via the derive(..) function
+ for (size_t i =0; i<new_ast->children.size(); i++)
+ {
+ AstNode *ch2 = new_ast->children[i];
+ if (ch2->type == AST_INTERFACEPORT) { // Is an interface port
+ std::string name_port = ch2->str; // Name of the interface port
+ if (ch2->children.size() > 0) {
+ for(size_t j=0; j<ch2->children.size();j++) {
+ AstNode *ch = ch2->children[j];
+ if(ch->type == AST_INTERFACEPORTTYPE) { // Found the AST node containing the type of the interface
+ std::pair<std::string,std::string> res = split_modport_from_type(ch->str);
+ std::string interface_type = res.first;
+ std::string interface_modport = res.second; // Is "", if no modport
+ if (design->modules_.count(interface_type) > 0) {
+ // Add a cell to the module corresponding to the interface port such that
+ // it can further propagated down if needed:
+ AstNode *celltype_for_intf = new AstNode(AST_CELLTYPE);
+ celltype_for_intf->str = interface_type;
+ AstNode *cell_for_intf = new AstNode(AST_CELL, celltype_for_intf);
+ cell_for_intf->str = name_port + "_inst_from_top_dummy";
+ new_ast->children.push_back(cell_for_intf);
+
+ // Get all members of this non-overridden dummy interface instance:
+ RTLIL::Module *intfmodule = design->modules_[interface_type]; // All interfaces should at this point in time (assuming
+ // reprocess_module is called from the hierarchy pass) be
+ // present in design->modules_
+ AstModule *ast_module_of_interface = (AstModule*)intfmodule;
+ std::string interface_modport_compare_str = "\\" + interface_modport;
+ AstNode *modport = find_modport(ast_module_of_interface->ast, interface_modport_compare_str); // modport == NULL if no modport
+ // Iterate over all wires in the interface and add them to the module:
+ explode_interface_port(new_ast, intfmodule, name_port, modport);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // The old module will be deleted. Rename and mark for deletion:
+ std::string original_name = this->name.str();
+ std::string changed_name = original_name + "_before_replacing_local_interfaces";
+ design->rename(this, changed_name);
+ this->set_bool_attribute("\\to_delete");
+
+ // Check if the module was the top module. If it was, we need to remove the top attribute and put it on the
+ // new module.
+ if (this->get_bool_attribute("\\initial_top")) {
+ this->attributes.erase("\\initial_top");
+ is_top = true;
+ }
+
+ // Generate RTLIL from AST for the new module and add to the design:
+ AstModule *newmod = process_module(new_ast, false, ast_before_replacing_interface_ports);
+ delete(new_ast);
+ design->add(newmod);
+ RTLIL::Module* mod = design->module(original_name);
+ if (is_top)
+ mod->set_bool_attribute("\\top");
+
+ // Set the attribute "interfaces_replaced_in_module" so that it does not happen again.
+ mod->set_bool_attribute("\\interfaces_replaced_in_module");
+}
+
+// create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces
+// This method is used to explode the interface when the interface is a port of the module (not instantiated inside)
+RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, dict<RTLIL::IdString, RTLIL::Module*> interfaces, dict<RTLIL::IdString, RTLIL::IdString> modports, bool mayfail)
+{
+ AstNode *new_ast = NULL;
+ std::string modname = derive_common(design, parameters, &new_ast, mayfail);
+
+ // Since interfaces themselves may be instantiated with different parameters,
+ // "modname" must also take those into account, so that unique modules
+ // are derived for any variant of interface connections:
+ std::string interf_info = "";
+
+ bool has_interfaces = false;
+ for(auto &intf : interfaces) {
+ interf_info += log_id(intf.second->name);
+ has_interfaces = true;
+ }
+
+ if (has_interfaces)
+ modname += "$interfaces$" + interf_info;
+
+
+ if (!design->has(modname)) {
+ new_ast->str = modname;
+
+ // Iterate over all interfaces which are ports in this module:
+ for(auto &intf : interfaces) {
+ RTLIL::Module * intfmodule = intf.second;
+ std::string intfname = intf.first.str();
+ // Check if a modport applies for the interface port:
+ AstNode *modport = NULL;
+ if (modports.count(intfname) > 0) {
+ std::string interface_modport = modports.at(intfname).str();
+ AstModule *ast_module_of_interface = (AstModule*)intfmodule;
+ AstNode *ast_node_of_interface = ast_module_of_interface->ast;
+ modport = find_modport(ast_node_of_interface, interface_modport);
+ }
+ // Iterate over all wires in the interface and add them to the module:
+ explode_interface_port(new_ast, intfmodule, intfname, modport);
+ }
+
+ design->add(process_module(new_ast, false));
+ design->module(modname)->check();
+
+ RTLIL::Module* mod = design->module(modname);
+
+ // Now that the interfaces have been exploded, we can delete the dummy port related to every interface.
+ for(auto &intf : interfaces) {
+ if(mod->wires_.count(intf.first)) {
+ mod->wires_.erase(intf.first);
+ mod->fixup_ports();
+ // We copy the cell of the interface to the sub-module such that it can further be found if it is propagated
+ // down to sub-sub-modules etc.
+ RTLIL::Cell * new_subcell = mod->addCell(intf.first, intf.second->name);
+ new_subcell->set_bool_attribute("\\is_interface");
+ }
+ else {
+ log_error("No port with matching name found (%s) in %s. Stopping\n", log_id(intf.first), modname.c_str());
+ }
+ }
+
+ // If any interfaces were replaced, set the attribute 'interfaces_replaced_in_module':
+ if (interfaces.size() > 0) {
+ mod->set_bool_attribute("\\interfaces_replaced_in_module");
+ }
+
+ } else {
+ log("Found cached RTLIL representation for module `%s'.\n", modname.c_str());
+ }
+
+ delete new_ast;
+ return modname;
+}
+
+// create a new parametric module (when needed) and return the name of the generated module - without support for interfaces
+RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail)
+{
+ AstNode *new_ast = NULL;
+ std::string modname = derive_common(design, parameters, &new_ast, mayfail);
+
+ if (!design->has(modname)) {
+ new_ast->str = modname;
+ design->add(process_module(new_ast, false));
+ design->module(modname)->check();
+ } else {
+ log("Found cached RTLIL representation for module `%s'.\n", modname.c_str());
+ }
+
+ delete new_ast;
+ return modname;
+}
+
// create a new parametric module (when needed) and return the name of the generated module
-RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool)
+std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool)
{
std::string stripped_name = name.str();
@@ -1096,7 +1367,8 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
current_ast = NULL;
flag_dump_ast1 = false;
flag_dump_ast2 = false;
- flag_dump_vlog = false;
+ flag_dump_vlog1 = false;
+ flag_dump_vlog2 = false;
flag_nolatches = nolatches;
flag_nomeminit = nomeminit;
flag_nomem2reg = nomem2reg;
@@ -1156,15 +1428,8 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict<RTLIL::IdString, R
else
modname = "$paramod" + stripped_name + para_info;
- if (!design->has(modname)) {
- new_ast->str = modname;
- design->add(process_module(new_ast, false));
- design->module(modname)->check();
- } else {
- log("Found cached RTLIL representation for module `%s'.\n", modname.c_str());
- }
- delete new_ast;
+ (*new_ast_out) = new_ast;
return modname;
}
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index 7e97bdb3..ddd59d4b 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -142,6 +142,11 @@ namespace AST
AST_NEGEDGE,
AST_EDGE,
+ AST_INTERFACE,
+ AST_INTERFACEPORT,
+ AST_INTERFACEPORTTYPE,
+ AST_MODPORT,
+ AST_MODPORTMEMBER,
AST_PACKAGE
};
@@ -209,6 +214,8 @@ namespace AST
MEM2REG_FL_SET_ASYNC = 0x00000800,
MEM2REG_FL_EQ2 = 0x00001000,
MEM2REG_FL_CMPLX_LHS = 0x00002000,
+ MEM2REG_FL_CONST_LHS = 0x00004000,
+ MEM2REG_FL_VAR_LHS = 0x00008000,
/* proc flags */
MEM2REG_FL_EQ1 = 0x01000000,
@@ -232,6 +239,7 @@ namespace AST
bool has_const_only_constructs(bool &recommend_const_eval);
void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall);
AstNode *eval_const_function(AstNode *fcall);
+ bool is_simple_const_expr();
// create a human-readable text representation of the AST (for debugging)
void dumpAst(FILE *f, std::string indent) const;
@@ -274,7 +282,7 @@ 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_vlog, bool dump_rtlil, bool nolatches, bool nomeminit,
+ void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_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);
// parametric modules are supported directly by the AST library
@@ -284,6 +292,9 @@ namespace AST
bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, 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;
+ std::string derive_common(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, AstNode **new_ast_out, bool mayfail);
+ void reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module *> local_interfaces) YS_OVERRIDE;
RTLIL::Module *clone() const YS_OVERRIDE;
};
@@ -300,6 +311,11 @@ namespace AST
// call a DPI function
AstNode *dpi_call(const std::string &rtype, const std::string &fname, const std::vector<std::string> &argtypes, const std::vector<AstNode*> &args);
+
+ // Helper functions related to handling SystemVerilog interfaces
+ std::pair<std::string,std::string> split_modport_from_type(std::string name_type);
+ AstNode * find_modport(AstNode *intf, std::string name);
+ void explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule, std::string intfname, AstNode *modport);
}
namespace AST_INTERNAL
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index 0f7e910f..b3a2a84b 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -55,8 +55,7 @@ static RTLIL::SigSpec uniop2rtlil(AstNode *that, std::string type, int result_wi
if (gen_attributes)
for (auto &attr : that->attributes) {
if (attr.second->type != AST_CONSTANT)
- log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n",
- attr.first.c_str());
+ log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
}
@@ -89,8 +88,7 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s
if (that != NULL)
for (auto &attr : that->attributes) {
if (attr.second->type != AST_CONSTANT)
- log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n",
- attr.first.c_str());
+ log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
}
@@ -117,8 +115,7 @@ static RTLIL::SigSpec binop2rtlil(AstNode *that, std::string type, int result_wi
for (auto &attr : that->attributes) {
if (attr.second->type != AST_CONSTANT)
- log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n",
- attr.first.c_str());
+ log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
}
@@ -152,8 +149,7 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const
for (auto &attr : that->attributes) {
if (attr.second->type != AST_CONSTANT)
- log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n",
- attr.first.c_str());
+ log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
cell->attributes[attr.first] = attr.second->asAttrConst();
}
@@ -480,8 +476,7 @@ struct AST_INTERNAL::ProcessGenerator
for (auto &attr : ast->attributes) {
if (attr.second->type != AST_CONSTANT)
- log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n",
- attr.first.c_str());
+ log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str());
sw->attributes[attr.first] = attr.second->asAttrConst();
}
@@ -530,7 +525,16 @@ struct AST_INTERNAL::ProcessGenerator
}
if (last_generated_case != NULL && ast->get_bool_attribute("\\full_case") && default_case == NULL) {
+ #if 0
+ // this is a valid transformation, but as optimization it is premature.
+ // better: add a default case that assigns 'x' to everything, and let later
+ // optimizations take care of the rest
last_generated_case->compare.clear();
+ #else
+ default_case = new RTLIL::CaseRule;
+ addChunkActions(default_case->actions, this_case_eq_ltemp, SigSpec(State::Sx, GetSize(this_case_eq_rvalue)));
+ sw->cases.push_back(default_case);
+ #endif
} else {
if (default_case == NULL) {
default_case = new RTLIL::CaseRule;
@@ -549,7 +553,11 @@ struct AST_INTERNAL::ProcessGenerator
break;
case AST_WIRE:
- log_file_error(ast->filename, ast->linenum, "Found wire declaration in block without label!\n");
+ log_file_error(ast->filename, ast->linenum, "Found reg declaration in block without label!\n");
+ break;
+
+ case AST_ASSIGN:
+ log_file_error(ast->filename, ast->linenum, "Found continous assignment in always/initial block!\n");
break;
case AST_PARAMETER:
@@ -648,9 +656,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
- log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n",
- str.c_str());
- this_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
+ log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
+ this_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
delete left_at_zero_ast;
delete right_at_zero_ast;
} else
@@ -778,7 +785,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { }
if (children[0]->type != AST_CONSTANT)
log_file_error(filename, linenum, "System function %s called with non-const argument!\n",
- RTLIL::unescape_id(str).c_str());
+ RTLIL::unescape_id(str).c_str());
width_hint = max(width_hint, int(children[0]->asInt(true)));
}
break;
@@ -798,9 +805,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun
// everything should have been handled above -> print error if not.
default:
for (auto f : log_files)
- current_ast->dumpAst(f, "verilog-ast> ");
- log_file_error(filename, linenum, "Don't know how to detect sign and width for %s node!\n",
- type2str(type).c_str());
+ current_ast_mod->dumpAst(f, "verilog-ast> ");
+ log_file_error(filename, linenum, "Don't know how to detect sign and width for %s node!\n", type2str(type).c_str());
}
if (*found_real)
@@ -853,6 +859,35 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
case AST_GENIF:
case AST_GENCASE:
case AST_PACKAGE:
+ case AST_MODPORT:
+ case AST_MODPORTMEMBER:
+ break;
+ case AST_INTERFACEPORT: {
+ // If a port in a module with unknown type is found, mark it with the attribute 'is_interface'
+ // This is used by the hierarchy pass to know when it can replace interface connection with the individual
+ // signals.
+ RTLIL::Wire *wire = current_module->addWire(str, 1);
+ wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+ wire->start_offset = 0;
+ wire->port_id = port_id;
+ wire->port_input = true;
+ wire->port_output = true;
+ wire->set_bool_attribute("\\is_interface");
+ if (children.size() > 0) {
+ for(size_t i=0; i<children.size();i++) {
+ if(children[i]->type == AST_INTERFACEPORTTYPE) {
+ std::pair<std::string,std::string> res = AST::split_modport_from_type(children[i]->str);
+ wire->attributes["\\interface_type"] = res.first;
+ if (res.second != "")
+ wire->attributes["\\interface_modport"] = res.second;
+ break;
+ }
+ }
+ }
+ wire->upto = 0;
+ }
+ break;
+ case AST_INTERFACEPORTTYPE:
break;
// remember the parameter, needed for example in techmap
@@ -863,11 +898,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// create an RTLIL::Wire for an AST_WIRE node
case AST_WIRE: {
if (current_module->wires_.count(str) != 0)
- log_file_error(filename, linenum, "Re-definition of signal `%s'!\n",
- str.c_str());
+ log_file_error(filename, linenum, "Re-definition of signal `%s'!\n", str.c_str());
if (!range_valid)
- log_file_error(filename, linenum, "Signal `%s' with non-constant width!\n",
- str.c_str());
+ 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));
@@ -881,8 +914,7 @@ 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());
wire->attributes[attr.first] = attr.second->asAttrConst();
}
}
@@ -891,16 +923,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// create an RTLIL::Memory for an AST_MEMORY node
case AST_MEMORY: {
if (current_module->memories.count(str) != 0)
- log_file_error(filename, linenum, "Re-definition of memory `%s'!\n",
- str.c_str());
+ log_file_error(filename, linenum, "Re-definition of memory `%s'!\n", str.c_str());
log_assert(children.size() >= 2);
log_assert(children[0]->type == AST_RANGE);
log_assert(children[1]->type == AST_RANGE);
if (!children[0]->range_valid || !children[1]->range_valid)
- log_file_error(filename, linenum, "Memory `%s' with non-constant width or size!\n",
- str.c_str());
+ log_file_error(filename, linenum, "Memory `%s' with non-constant width or size!\n", str.c_str());
RTLIL::Memory *memory = new RTLIL::Memory;
memory->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
@@ -917,8 +947,7 @@ 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());
memory->attributes[attr.first] = attr.second->asAttrConst();
}
}
@@ -926,19 +955,17 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// simply return the corresponding RTLIL::SigSpec for an AST_CONSTANT node
case AST_CONSTANT:
+ case AST_REALVALUE:
{
if (width_hint < 0)
detectSignWidth(width_hint, sign_hint);
-
is_signed = sign_hint;
- return RTLIL::SigSpec(bitsAsConst());
- }
- case AST_REALVALUE:
- {
+ if (type == AST_CONSTANT)
+ 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));
+ log_file_warning(filename, linenum, "converting real value %e to binary %s.\n", realvalue, log_signal(sig));
return sig;
}
@@ -949,6 +976,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
{
RTLIL::Wire *wire = NULL;
RTLIL::SigChunk chunk;
+ bool is_interface = false;
int add_undef_bits_msb = 0;
int add_undef_bits_lsb = 0;
@@ -964,19 +992,42 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
}
else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM) {
if (id2ast->children[0]->type != AST_CONSTANT)
- log_file_error(filename, linenum, "Parameter %s does not evaluate to constant value!\n",
- str.c_str());
+ log_file_error(filename, linenum, "Parameter %s does not evaluate to constant value!\n", str.c_str());
chunk = RTLIL::Const(id2ast->children[0]->bits);
goto use_const_chunk;
}
- else if (!id2ast || (id2ast->type != AST_WIRE && id2ast->type != AST_AUTOWIRE &&
- id2ast->type != AST_MEMORY) || current_module->wires_.count(str) == 0)
- log_file_error(filename, linenum, "Identifier `%s' doesn't map to any signal!\n",
- str.c_str());
+ else if (id2ast && (id2ast->type == AST_WIRE || id2ast->type == AST_AUTOWIRE || id2ast->type == AST_MEMORY) && current_module->wires_.count(str) != 0) {
+ RTLIL::Wire *current_wire = current_module->wire(str);
+ if (current_wire->get_bool_attribute("\\is_interface"))
+ is_interface = true;
+ // Ignore
+ }
+ // If an identifier is found that is not already known, assume that it is an interface:
+ else if (1) { // FIXME: Check if sv_mode first?
+ is_interface = true;
+ }
+ else {
+ log_file_error(filename, linenum, "Identifier `%s' doesn't map to any signal!\n", str.c_str());
+ }
if (id2ast->type == AST_MEMORY)
- log_file_error(filename, linenum, "Identifier `%s' does map to an unexpanded memory!\n",
- str.c_str());
+ log_file_error(filename, linenum, "Identifier `%s' does map to an unexpanded memory!\n", str.c_str());
+
+ // If identifier is an interface, create a RTLIL::SigSpec with a dummy wire with a attribute called 'is_interface'
+ // This makes it possible for the hierarchy pass to see what are interface connections and then replace them
+ // with the individual signals:
+ if (is_interface) {
+ RTLIL::Wire *dummy_wire;
+ std::string dummy_wire_name = "$dummywireforinterface" + str;
+ if (current_module->wires_.count(dummy_wire_name))
+ dummy_wire = current_module->wires_[dummy_wire_name];
+ else {
+ dummy_wire = current_module->addWire(dummy_wire_name);
+ dummy_wire->set_bool_attribute("\\is_interface");
+ }
+ RTLIL::SigSpec tmp = RTLIL::SigSpec(dummy_wire);
+ return tmp;
+ }
wire = current_module->wires_[str];
chunk.wire = wire;
@@ -985,7 +1036,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
use_const_chunk:
if (children.size() != 0) {
- log_assert(children[0]->type == AST_RANGE);
+ if (children[0]->type != AST_RANGE)
+ log_file_error(filename, linenum, "Single range expected.\n");
int source_width = id2ast->range_left - id2ast->range_right + 1;
int source_offset = id2ast->range_right;
if (!children[0]->range_valid) {
@@ -994,9 +1046,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
- log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n",
- str.c_str());
- int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
+ log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
+ int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ?
children[0]->children[1]->clone() : children[0]->children[0]->clone());
fake_ast->children[0]->delete_children();
@@ -1024,10 +1075,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
if (chunk.offset >= source_width || chunk.offset + chunk.width < 0) {
if (chunk.width == 1)
log_file_warning(filename, linenum, "Range select out of bounds on signal `%s': Setting result bit to undef.\n",
- str.c_str());
+ str.c_str());
else
- log_file_warning(filename, linenum, "Range select out of bounds on signal `%s': Setting all %d result bits to undef.\n",
- str.c_str(), chunk.width);
+ log_file_warning(filename, linenum, "Range select [%d:%d] out of bounds on signal `%s': Setting all %d result bits to undef.\n",
+ children[0]->range_left, children[0]->range_right, str.c_str(), chunk.width);
chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width);
} else {
if (chunk.width + chunk.offset > source_width) {
@@ -1040,11 +1091,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
chunk.offset += add_undef_bits_lsb;
}
if (add_undef_bits_lsb)
- log_file_warning(filename, linenum, "Range select out of bounds on signal `%s': Setting %d LSB bits to undef.\n",
- str.c_str(), add_undef_bits_lsb);
+ log_file_warning(filename, linenum, "Range [%d:%d] select out of bounds on signal `%s': Setting %d LSB bits to undef.\n",
+ children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_lsb);
if (add_undef_bits_msb)
- log_file_warning(filename, linenum, "Range select out of bounds on signal `%s': Setting %d MSB bits to undef.\n",
- str.c_str(), add_undef_bits_msb);
+ log_file_warning(filename, linenum, "Range [%d:%d] select out of bounds on signal `%s': Setting %d MSB bits to undef.\n",
+ children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_msb);
}
}
}
@@ -1371,16 +1422,21 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
if (GetSize(en) != 1)
en = current_module->ReduceBool(NEW_ID, en);
- std::stringstream sstr;
- sstr << celltype << "$" << filename << ":" << linenum << "$" << (autoidx++);
+ IdString cellname;
+ if (str.empty()) {
+ std::stringstream sstr;
+ sstr << celltype << "$" << filename << ":" << linenum << "$" << (autoidx++);
+ cellname = sstr.str();
+ } else {
+ cellname = str;
+ }
- RTLIL::Cell *cell = current_module->addCell(sstr.str(), celltype);
+ RTLIL::Cell *cell = current_module->addCell(cellname, celltype);
cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
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();
}
@@ -1402,9 +1458,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
new_right.append(right[i]);
}
log_file_warning(filename, linenum, "Ignoring assignment to constant bits:\n"
- " old assignment: %s = %s\n new assignment: %s = %s.\n",
- log_signal(left), log_signal(right),
- log_signal(new_left), log_signal(new_right));
+ " old assignment: %s = %s\n new assignment: %s = %s.\n",
+ log_signal(left), log_signal(right),
+ log_signal(new_left), log_signal(new_right));
left = new_left;
right = new_right;
}
@@ -1422,6 +1478,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
RTLIL::Cell *cell = current_module->addCell(str, "");
cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
+ // Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass
+ cell->set_bool_attribute("\\module_not_derived");
for (auto it = children.begin(); it != children.end(); it++) {
AstNode *child = *it;
@@ -1435,14 +1493,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
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);
+ log_id(cell), log_id(paraname), child->children[0]->realvalue);
auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue));
strnode->cloneInto(child->children[0]);
delete strnode;
}
if (child->children[0]->type != AST_CONSTANT)
log_file_error(filename, linenum, "Parameter %s.%s with non-constant value!\n",
- log_id(cell), log_id(paraname));
+ log_id(cell), log_id(paraname));
cell->parameters[paraname] = child->children[0]->asParaConst();
continue;
}
@@ -1463,8 +1521,7 @@ 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();
}
}
@@ -1492,18 +1549,17 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
if (GetSize(children) > 1)
log_file_error(filename, linenum, "System function %s got %d arguments, expected 1 or 0.\n",
- RTLIL::unescape_id(str).c_str(), GetSize(children));
+ RTLIL::unescape_id(str).c_str(), GetSize(children));
if (GetSize(children) == 1) {
if (children[0]->type != AST_CONSTANT)
log_file_error(filename, linenum, "System function %s called with non-const argument!\n",
- RTLIL::unescape_id(str).c_str());
+ RTLIL::unescape_id(str).c_str());
width = children[0]->asInt(true);
}
if (width <= 0)
- log_file_error(filename, linenum, "Failed to detect width of %s!\n",
- RTLIL::unescape_id(str).c_str());
+ log_file_error(filename, linenum, "Failed to detect width of %s!\n", RTLIL::unescape_id(str).c_str());
Cell *cell = current_module->addCell(myid, str.substr(1));
cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum);
@@ -1528,10 +1584,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
// everything should have been handled above -> print error if not.
default:
for (auto f : log_files)
- current_ast->dumpAst(f, "verilog-ast> ");
+ current_ast_mod->dumpAst(f, "verilog-ast> ");
type_name = type2str(type);
- log_file_error(filename, linenum, "Don't know how to generate RTLIL code for %s node!\n",
- type_name.c_str());
+ log_file_error(filename, linenum, "Don't know how to generate RTLIL code for %s node!\n", type_name.c_str());
}
return RTLIL::SigSpec();
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index aa3b982d..63b71b80 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -50,7 +50,6 @@ using namespace AST_INTERNAL;
bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param)
{
static int recursion_counter = 0;
- static pair<string, int> last_blocking_assignment_warn;
static bool deep_recursion_warning = false;
if (recursion_counter++ == 1000 && deep_recursion_warning) {
@@ -71,8 +70,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (stage == 0)
{
- log_assert(type == AST_MODULE);
- last_blocking_assignment_warn = pair<string, int>();
+ log_assert(type == AST_MODULE || type == AST_INTERFACE);
deep_recursion_warning = true;
while (simplify(const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint, in_param)) { }
@@ -113,6 +111,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (memflags & AstNode::MEM2REG_FL_CMPLX_LHS)
goto verbose_activate;
+ if ((memflags & AstNode::MEM2REG_FL_CONST_LHS) && !(memflags & AstNode::MEM2REG_FL_VAR_LHS))
+ goto verbose_activate;
+
// log("Note: Not replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags));
continue;
@@ -137,9 +138,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
int mem_width, mem_size, addr_bits;
node->meminfo(mem_width, mem_size, addr_bits);
+ int data_range_left = node->children[0]->range_left;
+ int data_range_right = node->children[0]->range_right;
+
+ if (node->children[0]->range_swapped)
+ std::swap(data_range_left, data_range_right);
+
for (int i = 0; i < mem_size; i++) {
AstNode *reg = new AstNode(AST_WIRE, new AstNode(AST_RANGE,
- mkconst_int(mem_width-1, true), mkconst_int(0, true)));
+ mkconst_int(data_range_left, true), mkconst_int(data_range_right, true)));
reg->str = stringf("%s[%d]", node->str.c_str(), i);
reg->is_reg = true;
reg->is_signed = node->is_signed;
@@ -196,7 +203,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
int nargs = GetSize(children);
if (nargs < 1)
log_file_error(filename, linenum, "System task `%s' got %d arguments, expected >= 1.\n",
- str.c_str(), int(children.size()));
+ str.c_str(), int(children.size()));
// First argument is the format string
AstNode *node_string = children[0];
@@ -240,7 +247,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
case 'X':
if (next_arg >= GetSize(children))
log_file_error(filename, linenum, "Missing argument for %%%c format specifier in system task `%s'.\n",
- cformat, str.c_str());
+ cformat, str.c_str());
node_arg = children[next_arg++];
while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
@@ -325,6 +332,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
for (size_t i = 0; i < children.size(); i++) {
AstNode *node = children[i];
if (node->type == AST_WIRE) {
+ if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) {
+ for (auto c : node->children[0]->children) {
+ if (!c->is_simple_const_expr()) {
+ if (attributes.count("\\dynports"))
+ delete attributes.at("\\dynports");
+ attributes["\\dynports"] = AstNode::mkconst_int(1, true);
+ }
+ }
+ }
if (this_wire_scope.count(node->str) > 0) {
AstNode *first_node = this_wire_scope[node->str];
if (first_node->is_input && node->is_reg)
@@ -450,8 +466,21 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
children[0]->id2ast->is_reg = true; // if logic type is used in a block asignment
if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && !children[0]->id2ast->is_reg)
log_warning("wire '%s' is assigned in a block at %s:%d.\n", children[0]->str.c_str(), filename.c_str(), linenum);
- if (type == AST_ASSIGN && children[0]->id2ast->is_reg)
- log_warning("reg '%s' is assigned in a continuous assignment at %s:%d.\n", children[0]->str.c_str(), filename.c_str(), linenum);
+ if (type == AST_ASSIGN && children[0]->id2ast->is_reg) {
+ bool is_rand_reg = false;
+ if (children[1]->type == AST_FCALL) {
+ if (children[1]->str == "\\$anyconst")
+ is_rand_reg = true;
+ if (children[1]->str == "\\$anyseq")
+ is_rand_reg = true;
+ if (children[1]->str == "\\$allconst")
+ is_rand_reg = true;
+ if (children[1]->str == "\\$allseq")
+ is_rand_reg = true;
+ }
+ if (!is_rand_reg)
+ log_warning("reg '%s' is assigned in a continuous assignment at %s:%d.\n", children[0]->str.c_str(), filename.c_str(), linenum);
+ }
children[0]->was_checked = true;
}
break;
@@ -629,6 +658,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
// (iterate by index as e.g. auto wires can add new children in the process)
for (size_t i = 0; i < children.size(); i++) {
bool did_something_here = true;
+ bool backup_flag_autowire = flag_autowire;
if ((type == AST_GENFOR || type == AST_FOR) && i >= 3)
break;
if ((type == AST_GENIF || type == AST_GENCASE) && i >= 1)
@@ -639,6 +669,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
break;
if (type == AST_PREFIX && i >= 1)
break;
+ if (type == AST_DEFPARAM && i == 0)
+ flag_autowire = true;
while (did_something_here && i < children.size()) {
bool const_fold_here = const_fold, in_lvalue_here = in_lvalue;
int width_hint_here = width_hint;
@@ -673,6 +705,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
children.erase(children.begin() + (i--));
did_something = true;
}
+ flag_autowire = backup_flag_autowire;
}
for (auto &attr : attributes) {
while (attr.second->simplify(true, false, false, stage, -1, false, true))
@@ -732,7 +765,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (current_scope.at(modname)->type != AST_CELL)
log_file_error(filename, linenum, "Defparam argument `%s . %s` does not match a cell!\n",
- RTLIL::unescape_id(modname).c_str(), RTLIL::unescape_id(paramname).c_str());
+ RTLIL::unescape_id(modname).c_str(), RTLIL::unescape_id(paramname).c_str());
AstNode *paraset = new AstNode(AST_PARASET, children[1]->clone(), GetSize(children) > 2 ? children[2]->clone() : NULL);
paraset->str = paramname;
@@ -880,7 +913,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (children[0]->type == AST_REALVALUE) {
RTLIL::Const constvalue = children[0]->realAsConst(width);
log_file_warning(filename, linenum, "converting real value %e to binary %s.\n",
- children[0]->realvalue, log_signal(constvalue));
+ children[0]->realvalue, log_signal(constvalue));
delete children[0];
children[0] = mkconst_bits(constvalue.bits, sign_hint);
did_something = true;
@@ -921,12 +954,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
}
if (current_scope.count(str) == 0) {
- // log_warning("Creating auto-wire `%s' in module `%s'.\n", str.c_str(), current_ast_mod->str.c_str());
- AstNode *auto_wire = new AstNode(AST_AUTOWIRE);
- auto_wire->str = str;
- current_ast_mod->children.push_back(auto_wire);
- current_scope[str] = auto_wire;
- did_something = true;
+ if (flag_autowire || str == "\\$global_clock") {
+ AstNode *auto_wire = new AstNode(AST_AUTOWIRE);
+ auto_wire->str = str;
+ current_ast_mod->children.push_back(auto_wire);
+ current_scope[str] = auto_wire;
+ did_something = true;
+ } else {
+ log_file_error(filename, linenum, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str());
+ }
}
if (id2ast != current_scope[str]) {
id2ast = current_scope[str];
@@ -946,6 +982,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
int data_range_left = id2ast->children[0]->range_left;
int data_range_right = id2ast->children[0]->range_right;
+ if (id2ast->children[0]->range_swapped)
+ std::swap(data_range_left, data_range_right);
+
std::stringstream sstr;
sstr << "$mem2bits$" << str << "$" << filename << ":" << linenum << "$" << (autoidx++);
std::string wire_id = sstr.str();
@@ -1299,8 +1338,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (type == AST_PRIMITIVE)
{
if (children.size() < 2)
- log_file_error(filename, linenum, "Insufficient number of arguments for primitive `%s'!\n",
- str.c_str());
+ log_file_error(filename, linenum, "Insufficient number of arguments for primitive `%s'!\n", str.c_str());
std::vector<AstNode*> children_list;
for (auto child : children) {
@@ -1315,8 +1353,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (str == "bufif0" || str == "bufif1" || str == "notif0" || str == "notif1")
{
if (children_list.size() != 3)
- log_file_error(filename, linenum, "Invalid number of arguments for primitive `%s'!\n",
- str.c_str());
+ log_file_error(filename, linenum, "Invalid number of arguments for primitive `%s'!\n", str.c_str());
std::vector<RTLIL::State> z_const(1, RTLIL::State::Sz);
@@ -1336,6 +1373,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
str.clear();
type = AST_ASSIGN;
children.push_back(children_list.at(0));
+ children.back()->was_checked = true;
children.push_back(node);
did_something = true;
}
@@ -1372,6 +1410,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
str.clear();
type = AST_ASSIGN;
children.push_back(children_list[0]);
+ children.back()->was_checked = true;
children.push_back(node);
did_something = true;
}
@@ -1401,8 +1440,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
while (left_at_zero_ast->simplify(true, true, false, stage, -1, false, false)) { }
while (right_at_zero_ast->simplify(true, true, false, stage, -1, false, false)) { }
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
- log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n",
- str.c_str());
+ log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
}
did_something = true;
@@ -1480,6 +1518,7 @@ skip_dynamic_range_lvalue_expansion:;
newNode->children.push_back(assign_en);
AstNode *assertnode = new AstNode(type);
+ assertnode->str = str;
assertnode->children.push_back(new AstNode(AST_IDENTIFIER));
assertnode->children.push_back(new AstNode(AST_IDENTIFIER));
assertnode->children[0]->str = id_check;
@@ -1530,6 +1569,7 @@ skip_dynamic_range_lvalue_expansion:;
wire_tmp_id->str = wire_tmp->str;
newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, wire_tmp_id, children[1]->clone()));
+ newNode->children.back()->was_checked = true;
int cursor = 0;
for (auto child : children[0]->children)
@@ -1559,14 +1599,6 @@ skip_dynamic_range_lvalue_expansion:;
sstr << "$memwr$" << children[0]->str << "$" << filename << ":" << linenum << "$" << (autoidx++);
std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN";
- if (type == AST_ASSIGN_EQ) {
- pair<string, int> this_blocking_assignment_warn(filename, linenum);
- if (this_blocking_assignment_warn != last_blocking_assignment_warn)
- log_warning("Blocking assignment to memory in line %s:%d is handled like a non-blocking assignment.\n",
- filename.c_str(), linenum);
- last_blocking_assignment_warn = this_blocking_assignment_warn;
- }
-
int mem_width, mem_size, addr_bits;
bool mem_signed = children[0]->id2ast->is_signed;
children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits);
@@ -1676,7 +1708,7 @@ skip_dynamic_range_lvalue_expansion:;
while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { }
if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT)
log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str());
- int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1;
+ int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1;
assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER),
new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone()));
@@ -1765,18 +1797,18 @@ skip_dynamic_range_lvalue_expansion:;
if (str == "\\$past")
{
- if (width_hint <= 0)
+ if (width_hint < 0)
goto replace_fcall_later;
int num_steps = 1;
if (GetSize(children) != 1 && GetSize(children) != 2)
log_file_error(filename, linenum, "System function %s got %d arguments, expected 1 or 2.\n",
- RTLIL::unescape_id(str).c_str(), int(children.size()));
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
if (!current_always_clocked)
log_file_error(filename, linenum, "System function %s is only allowed in clocked blocks.\n",
- RTLIL::unescape_id(str).c_str());
+ RTLIL::unescape_id(str).c_str());
if (GetSize(children) == 2)
{
@@ -1797,6 +1829,11 @@ skip_dynamic_range_lvalue_expansion:;
log_assert(block != nullptr);
+ if (num_steps == 0) {
+ newNode = children[0]->clone();
+ goto apply_newNode;
+ }
+
int myidx = autoidx++;
AstNode *outreg = nullptr;
@@ -1815,6 +1852,7 @@ skip_dynamic_range_lvalue_expansion:;
AstNode *regid = new AstNode(AST_IDENTIFIER);
regid->str = reg->str;
regid->id2ast = reg;
+ regid->was_checked = true;
AstNode *rhs = nullptr;
@@ -1836,15 +1874,15 @@ skip_dynamic_range_lvalue_expansion:;
goto apply_newNode;
}
- if (str == "\\$stable" || str == "\\$rose" || str == "\\$fell")
+ if (str == "\\$stable" || str == "\\$rose" || str == "\\$fell" || str == "\\$changed")
{
if (GetSize(children) != 1)
log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n",
- RTLIL::unescape_id(str).c_str(), int(children.size()));
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
if (!current_always_clocked)
log_file_error(filename, linenum, "System function %s is only allowed in clocked blocks.\n",
- RTLIL::unescape_id(str).c_str());
+ RTLIL::unescape_id(str).c_str());
AstNode *present = children.at(0)->clone();
AstNode *past = clone();
@@ -1853,11 +1891,18 @@ skip_dynamic_range_lvalue_expansion:;
if (str == "\\$stable")
newNode = new AstNode(AST_EQ, past, present);
+ else if (str == "\\$changed")
+ newNode = new AstNode(AST_NE, past, present);
+
else if (str == "\\$rose")
- newNode = new AstNode(AST_LOGIC_AND, new AstNode(AST_LOGIC_NOT, past), present);
+ newNode = new AstNode(AST_LOGIC_AND,
+ new AstNode(AST_LOGIC_NOT, new AstNode(AST_BIT_AND, past, mkconst_int(1,false))),
+ new AstNode(AST_BIT_AND, present, mkconst_int(1,false)));
else if (str == "\\$fell")
- newNode = new AstNode(AST_LOGIC_AND, past, new AstNode(AST_LOGIC_NOT, present));
+ newNode = new AstNode(AST_LOGIC_AND,
+ new AstNode(AST_BIT_AND, past, mkconst_int(1,false)),
+ new AstNode(AST_LOGIC_NOT, new AstNode(AST_BIT_AND, present, mkconst_int(1,false))));
else
log_abort();
@@ -1875,7 +1920,7 @@ skip_dynamic_range_lvalue_expansion:;
{
if (children.size() != 1)
log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n",
- RTLIL::unescape_id(str).c_str(), int(children.size()));
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
AstNode *buf = children[0]->clone();
while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
@@ -1892,7 +1937,7 @@ skip_dynamic_range_lvalue_expansion:;
if (arg_value.bits.at(i) == RTLIL::State::S1)
result = i + 1;
- newNode = mkconst_int(result, false);
+ newNode = mkconst_int(result, true);
goto apply_newNode;
}
@@ -1900,11 +1945,11 @@ skip_dynamic_range_lvalue_expansion:;
{
if (str == "\\$bits" && children.size() != 1)
log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n",
- RTLIL::unescape_id(str).c_str(), int(children.size()));
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
if (str == "\\$size" && children.size() != 1 && children.size() != 2)
log_file_error(filename, linenum, "System function %s got %d arguments, expected 1 or 2.\n",
- RTLIL::unescape_id(str).c_str(), int(children.size()));
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
int dim = 1;
if (str == "\\$size" && children.size() == 2) {
@@ -1978,18 +2023,18 @@ skip_dynamic_range_lvalue_expansion:;
if (func_with_two_arguments) {
if (children.size() != 2)
log_file_error(filename, linenum, "System function %s got %d arguments, expected 2.\n",
- RTLIL::unescape_id(str).c_str(), int(children.size()));
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
} else {
if (children.size() != 1)
log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n",
- RTLIL::unescape_id(str).c_str(), int(children.size()));
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
}
if (children.size() >= 1) {
while (children[0]->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (!children[0]->isConst())
log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant argument.\n",
- RTLIL::unescape_id(str).c_str());
+ RTLIL::unescape_id(str).c_str());
int child_width_hint = width_hint;
bool child_sign_hint = sign_hint;
children[0]->detectSignWidth(child_width_hint, child_sign_hint);
@@ -2000,7 +2045,7 @@ skip_dynamic_range_lvalue_expansion:;
while (children[1]->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
if (!children[1]->isConst())
log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant argument.\n",
- RTLIL::unescape_id(str).c_str());
+ RTLIL::unescape_id(str).c_str());
int child_width_hint = width_hint;
bool child_sign_hint = sign_hint;
children[1]->detectSignWidth(child_width_hint, child_sign_hint);
@@ -2088,7 +2133,7 @@ skip_dynamic_range_lvalue_expansion:;
{
if (GetSize(children) < 2 || GetSize(children) > 4)
log_file_error(filename, linenum, "System function %s got %d arguments, expected 2-4.\n",
- RTLIL::unescape_id(str).c_str(), int(children.size()));
+ RTLIL::unescape_id(str).c_str(), int(children.size()));
AstNode *node_filename = children[0]->clone();
while (node_filename->simplify(true, false, false, stage, width_hint, sign_hint, false)) { }
@@ -2136,6 +2181,8 @@ skip_dynamic_range_lvalue_expansion:;
}
newNode = readmem(str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr, unconditional_init);
+ delete node_filename;
+ delete node_memory;
goto apply_newNode;
}
@@ -2177,6 +2224,8 @@ skip_dynamic_range_lvalue_expansion:;
std::map<std::string, std::string> replace_rules;
vector<AstNode*> added_mod_children;
dict<std::string, AstNode*> wire_cache;
+ vector<AstNode*> new_stmts;
+ vector<AstNode*> output_assignments;
if (current_block == NULL)
{
@@ -2201,6 +2250,8 @@ skip_dynamic_range_lvalue_expansion:;
AstNode *always = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK,
new AstNode(AST_ASSIGN_EQ, lvalue, clone())));
+ always->children[0]->children[0]->was_checked = true;
+
current_ast_mod->children.push_back(always);
goto replace_fcall_with_id;
@@ -2250,6 +2301,7 @@ skip_dynamic_range_lvalue_expansion:;
AstNode *assign = child->is_input ?
new AstNode(AST_ASSIGN_EQ, wire_id->clone(), arg) :
new AstNode(AST_ASSIGN_EQ, arg, wire_id->clone());
+ assign->children[0]->was_checked = true;
for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) {
if (*it != current_block_child)
@@ -2298,8 +2350,8 @@ skip_dynamic_range_lvalue_expansion:;
wire->port_id = 0;
wire->is_input = false;
wire->is_output = false;
- if (!child->is_output)
- wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
+ wire->is_reg = true;
+ wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false);
wire_cache[child->str] = wire;
current_ast_mod->children.push_back(wire);
@@ -2320,13 +2372,11 @@ skip_dynamic_range_lvalue_expansion:;
AstNode *assign = child->is_input ?
new AstNode(AST_ASSIGN_EQ, wire_id, arg) :
new AstNode(AST_ASSIGN_EQ, arg, wire_id);
-
- for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) {
- if (*it != current_block_child)
- continue;
- current_block->children.insert(it, assign);
- break;
- }
+ assign->children[0]->was_checked = true;
+ if (child->is_input)
+ new_stmts.push_back(assign);
+ else
+ output_assignments.push_back(assign);
}
}
@@ -2340,14 +2390,18 @@ skip_dynamic_range_lvalue_expansion:;
{
AstNode *stmt = child->clone();
stmt->replace_ids(prefix, replace_rules);
+ new_stmts.push_back(stmt);
+ }
- for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) {
- if (*it != current_block_child)
- continue;
- current_block->children.insert(it, stmt);
- break;
- }
+ new_stmts.insert(new_stmts.end(), output_assignments.begin(), output_assignments.end());
+
+ for (auto it = current_block->children.begin(); ; it++) {
+ log_assert(it != current_block->children.end());
+ if (*it == current_block_child) {
+ current_block->children.insert(it, new_stmts.begin(), new_stmts.end());
+ break;
}
+ }
replace_fcall_with_id:
if (type == AST_FCALL) {
@@ -2759,6 +2813,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor, false))), value));
block->children.back()->children[0]->str = memory->str;
block->children.back()->children[0]->id2ast = memory;
+ block->children.back()->children[0]->was_checked = true;
}
cursor += increment;
@@ -2817,7 +2872,11 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
for (size_t i = 0; i < children.size(); i++) {
AstNode *child = children[i];
- if (child->type != AST_FUNCTION && child->type != AST_TASK && child->type != AST_PREFIX)
+ // AST_PREFIX member names should not be prefixed; a nested AST_PREFIX
+ // still needs to recursed-into
+ if (type == AST_PREFIX && i == 1 && child->type == AST_IDENTIFIER)
+ continue;
+ if (child->type != AST_FUNCTION && child->type != AST_TASK)
child->expand_genblock(index_var, prefix, name_map);
}
@@ -2872,7 +2931,7 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg
dict<AstNode*, uint32_t> &mem2reg_candidates, dict<AstNode*, uint32_t> &proc_flags, uint32_t &flags)
{
uint32_t children_flags = 0;
- int ignore_children_counter = 0;
+ int lhs_children_counter = 0;
if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ)
{
@@ -2898,6 +2957,16 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg
proc_flags[mem] |= AstNode::MEM2REG_FL_EQ1;
}
+ // for proper (non-init) writes: remember if this is a constant index or not
+ if ((flags & MEM2REG_FL_INIT) == 0) {
+ if (children[0]->children.size() && children[0]->children[0]->type == AST_RANGE && children[0]->children[0]->children.size()) {
+ if (children[0]->children[0]->children[0]->type == AST_CONSTANT)
+ mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CONST_LHS;
+ else
+ mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_VAR_LHS;
+ }
+ }
+
// remember where this is
if (flags & MEM2REG_FL_INIT) {
if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_INIT))
@@ -2910,7 +2979,7 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg
}
}
- ignore_children_counter = 1;
+ lhs_children_counter = 1;
}
if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY)
@@ -2953,12 +3022,23 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg
log_assert((flags & ~0x000000ff) == 0);
for (auto child : children)
- if (ignore_children_counter > 0)
- ignore_children_counter--;
- else if (proc_flags_p)
+ {
+ if (lhs_children_counter > 0) {
+ lhs_children_counter--;
+ if (child->children.size() && child->children[0]->type == AST_RANGE && child->children[0]->children.size()) {
+ for (auto c : child->children[0]->children) {
+ if (proc_flags_p)
+ c->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, *proc_flags_p, flags);
+ else
+ c->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, proc_flags, flags);
+ }
+ }
+ } else
+ if (proc_flags_p)
child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, *proc_flags_p, flags);
else
child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, proc_flags, flags);
+ }
flags &= ~children_flags | backup_flags;
@@ -3010,6 +3090,39 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
if (type == AST_FUNCTION || type == AST_TASK)
return false;
+ if (type == AST_MEMINIT && id2ast && mem2reg_set.count(id2ast))
+ {
+ log_assert(children[0]->type == AST_CONSTANT);
+ log_assert(children[1]->type == AST_CONSTANT);
+ log_assert(children[2]->type == AST_CONSTANT);
+
+ int cursor = children[0]->asInt(false);
+ Const data = children[1]->bitsAsConst();
+ int length = children[2]->asInt(false);
+
+ if (length != 0)
+ {
+ AstNode *block = new AstNode(AST_INITIAL, new AstNode(AST_BLOCK));
+ mod->children.push_back(block);
+ block = block->children[0];
+
+ int wordsz = GetSize(data) / length;
+
+ for (int i = 0; i < length; i++) {
+ block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+i, false))), mkconst_bits(data.extract(i*wordsz, wordsz).bits, false)));
+ block->children.back()->children[0]->str = str;
+ block->children.back()->children[0]->id2ast = id2ast;
+ block->children.back()->children[0]->was_checked = true;
+ }
+ }
+
+ AstNode *newNode = new AstNode(AST_NONE);
+ newNode->cloneInto(this);
+ delete newNode;
+
+ did_something = true;
+ }
+
if (type == AST_ASSIGN && block == NULL && children[0]->mem2reg_check(mem2reg_set))
{
if (async_block == NULL) {
@@ -3019,6 +3132,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
AstNode *newNode = clone();
newNode->type = AST_ASSIGN_EQ;
+ newNode->children[0]->was_checked = true;
async_block->children[0]->children.push_back(newNode);
newNode = new AstNode(AST_NONE);
@@ -3064,6 +3178,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone());
assign_addr->children[0]->str = id_addr;
+ assign_addr->children[0]->was_checked = true;
block->children.insert(block->children.begin()+assign_idx+1, assign_addr);
AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER));
@@ -3087,6 +3202,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod,
children[0]->id2ast = NULL;
children[0]->str = id_data;
type = AST_ASSIGN_EQ;
+ children[0]->was_checked = true;
did_something = true;
}
@@ -3236,6 +3352,16 @@ bool AstNode::has_const_only_constructs(bool &recommend_const_eval)
return false;
}
+bool AstNode::is_simple_const_expr()
+{
+ if (type == AST_IDENTIFIER)
+ return false;
+ for (auto child : children)
+ if (!child->is_simple_const_expr())
+ return false;
+ return true;
+}
+
// helper function for AstNode::eval_const_function()
void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall)
{
@@ -3244,12 +3370,12 @@ void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &varia
if (!children.empty()) {
if (children.size() != 1 || children.at(0)->type != AST_RANGE)
log_file_error(filename, linenum, "Memory access in constant function is not supported\n%s:%d: ...called from here.\n",
- fcall->filename.c_str(), fcall->linenum);
+ fcall->filename.c_str(), fcall->linenum);
children.at(0)->replace_variables(variables, fcall);
while (simplify(true, false, false, 1, -1, false, true)) { }
if (!children.at(0)->range_valid)
log_file_error(filename, linenum, "Non-constant range\n%s:%d: ... called from here.\n",
- fcall->filename.c_str(), fcall->linenum);
+ fcall->filename.c_str(), fcall->linenum);
offset = min(children.at(0)->range_left, children.at(0)->range_right);
width = min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width);
}
@@ -3289,7 +3415,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
while (child->simplify(true, false, false, 1, -1, false, true)) { }
if (!child->range_valid)
log_file_error(child->filename, child->linenum, "Can't determine size of variable %s\n%s:%d: ... called from here.\n",
- child->str.c_str(), fcall->filename.c_str(), fcall->linenum);
+ child->str.c_str(), fcall->filename.c_str(), fcall->linenum);
variables[child->str].val = RTLIL::Const(RTLIL::State::Sx, abs(child->range_left - child->range_right)+1);
variables[child->str].offset = min(child->range_left, child->range_right);
variables[child->str].is_signed = child->is_signed;
@@ -3333,15 +3459,15 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
if (stmt->children.at(1)->type != AST_CONSTANT)
log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here. X\n",
- fcall->filename.c_str(), fcall->linenum);
+ fcall->filename.c_str(), fcall->linenum);
if (stmt->children.at(0)->type != AST_IDENTIFIER)
log_file_error(stmt->filename, stmt->linenum, "Unsupported composite left hand side in constant function\n%s:%d: ... called from here.\n",
- fcall->filename.c_str(), fcall->linenum);
+ fcall->filename.c_str(), fcall->linenum);
if (!variables.count(stmt->children.at(0)->str))
log_file_error(stmt->filename, stmt->linenum, "Assignment to non-local variable in constant function\n%s:%d: ... called from here.\n",
- fcall->filename.c_str(), fcall->linenum);
+ fcall->filename.c_str(), fcall->linenum);
if (stmt->children.at(0)->children.empty()) {
variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.bits.size());
@@ -3349,7 +3475,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
AstNode *range = stmt->children.at(0)->children.at(0);
if (!range->range_valid)
log_file_error(range->filename, range->linenum, "Non-constant range\n%s:%d: ... called from here.\n",
- fcall->filename.c_str(), fcall->linenum);
+ fcall->filename.c_str(), fcall->linenum);
int offset = min(range->range_left, range->range_right);
int width = std::abs(range->range_left - range->range_right) + 1;
varinfo_t &v = variables[stmt->children.at(0)->str];
@@ -3381,7 +3507,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
if (cond->type != AST_CONSTANT)
log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here.\n",
- fcall->filename.c_str(), fcall->linenum);
+ fcall->filename.c_str(), fcall->linenum);
if (cond->asBool()) {
block->children.insert(block->children.begin(), stmt->children.at(1)->clone());
@@ -3402,7 +3528,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
if (num->type != AST_CONSTANT)
log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here.\n",
- fcall->filename.c_str(), fcall->linenum);
+ fcall->filename.c_str(), fcall->linenum);
block->children.erase(block->children.begin());
for (int i = 0; i < num->bitsAsConst().as_int(); i++)
@@ -3440,7 +3566,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
if (cond->type != AST_CONSTANT)
log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here.\n",
- fcall->filename.c_str(), fcall->linenum);
+ fcall->filename.c_str(), fcall->linenum);
found_match = cond->asBool();
delete cond;
@@ -3470,7 +3596,7 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
}
log_file_error(stmt->filename, stmt->linenum, "Unsupported language construct in constant function\n%s:%d: ... called from here.\n",
- fcall->filename.c_str(), fcall->linenum);
+ fcall->filename.c_str(), fcall->linenum);
log_abort();
}
diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc
index 034b3e70..a6a07863 100644
--- a/frontends/blif/blifparse.cc
+++ b/frontends/blif/blifparse.cc
@@ -276,7 +276,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo
if(lastcell == nullptr || module == nullptr)
{
- err_reason = stringf("No primative object to attach .cname %s.", p);
+ err_reason = stringf("No primitive object to attach .cname %s.", p);
goto error_with_reason;
}
@@ -584,7 +584,7 @@ struct BlifFrontend : public Frontend {
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" read_blif [filename]\n");
+ log(" read_blif [options] [filename]\n");
log("\n");
log("Load modules from a BLIF file into the current design.\n");
log("\n");
diff --git a/frontends/ilang/ilang_frontend.cc b/frontends/ilang/ilang_frontend.cc
index d8783ac1..6b302a79 100644
--- a/frontends/ilang/ilang_frontend.cc
+++ b/frontends/ilang/ilang_frontend.cc
@@ -44,11 +44,39 @@ struct IlangFrontend : public Frontend {
log("Load modules from an ilang file to the current design. (ilang is a text\n");
log("representation of a design in yosys's internal format.)\n");
log("\n");
+ log(" -nooverwrite\n");
+ log(" ignore re-definitions of modules. (the default behavior is to\n");
+ log(" create an error message if the existing module is not a blackbox\n");
+ log(" module, and overwrite the existing module if it is a blackbox module.)\n");
+ log("\n");
+ log(" -overwrite\n");
+ log(" overwrite existing modules with the same name\n");
+ log("\n");
}
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
+ ILANG_FRONTEND::flag_nooverwrite = false;
+ ILANG_FRONTEND::flag_overwrite = false;
+
log_header(design, "Executing ILANG frontend.\n");
- extra_args(f, filename, args, 1);
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ std::string arg = args[argidx];
+ if (arg == "-nooverwrite") {
+ ILANG_FRONTEND::flag_nooverwrite = true;
+ ILANG_FRONTEND::flag_overwrite = false;
+ continue;
+ }
+ if (arg == "-overwrite") {
+ ILANG_FRONTEND::flag_nooverwrite = false;
+ ILANG_FRONTEND::flag_overwrite = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
log("Input filename: %s\n", filename.c_str());
ILANG_FRONTEND::lexin = f;
diff --git a/frontends/ilang/ilang_frontend.h b/frontends/ilang/ilang_frontend.h
index ad3ffec9..052dd4cb 100644
--- a/frontends/ilang/ilang_frontend.h
+++ b/frontends/ilang/ilang_frontend.h
@@ -32,6 +32,8 @@ YOSYS_NAMESPACE_BEGIN
namespace ILANG_FRONTEND {
extern std::istream *lexin;
extern RTLIL::Design *current_design;
+ extern bool flag_nooverwrite;
+ extern bool flag_overwrite;
}
YOSYS_NAMESPACE_END
diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y
index bfc062fe..5bcc01f4 100644
--- a/frontends/ilang/ilang_parser.y
+++ b/frontends/ilang/ilang_parser.y
@@ -37,6 +37,8 @@ namespace ILANG_FRONTEND {
std::vector<std::vector<RTLIL::SwitchRule*>*> switch_stack;
std::vector<RTLIL::CaseRule*> case_stack;
dict<RTLIL::IdString, RTLIL::Const> attrbuf;
+ bool flag_nooverwrite, flag_overwrite;
+ bool delete_current_module;
}
using namespace ILANG_FRONTEND;
YOSYS_NAMESPACE_END
@@ -93,18 +95,36 @@ design:
module:
TOK_MODULE TOK_ID EOL {
- if (current_design->has($2))
- rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of module %s.", $2).c_str());
+ delete_current_module = false;
+ if (current_design->has($2)) {
+ RTLIL::Module *existing_mod = current_design->module($2);
+ if (!flag_overwrite && attrbuf.count("\\blackbox") && attrbuf.at("\\blackbox").as_bool()) {
+ log("Ignoring blackbox re-definition of module %s.\n", $2);
+ delete_current_module = true;
+ } else if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute("\\blackbox")) {
+ rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of module %s.", $2).c_str());
+ } else if (flag_nooverwrite) {
+ log("Ignoring re-definition of module %s.\n", $2);
+ delete_current_module = true;
+ } else {
+ log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute("\\blackbox") ? " blackbox" : "", $2);
+ current_design->remove(existing_mod);
+ }
+ }
current_module = new RTLIL::Module;
current_module->name = $2;
current_module->attributes = attrbuf;
- current_design->add(current_module);
+ if (!delete_current_module)
+ current_design->add(current_module);
attrbuf.clear();
free($2);
} module_body TOK_END {
if (attrbuf.size() != 0)
rtlil_frontend_ilang_yyerror("dangling attribute");
current_module->fixup_ports();
+ if (delete_current_module)
+ delete current_module;
+ current_module = nullptr;
} EOL;
module_body:
@@ -387,17 +407,13 @@ sigspec:
$$ = new RTLIL::SigSpec(current_module->wires_[$1]);
free($1);
} |
- TOK_ID '[' TOK_INT ']' {
- if (current_module->wires_.count($1) == 0)
- rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str());
- $$ = new RTLIL::SigSpec(current_module->wires_[$1], $3);
- free($1);
+ sigspec '[' TOK_INT ']' {
+ $$ = new RTLIL::SigSpec($1->extract($3));
+ delete $1;
} |
- TOK_ID '[' TOK_INT ':' TOK_INT ']' {
- if (current_module->wires_.count($1) == 0)
- rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str());
- $$ = new RTLIL::SigSpec(current_module->wires_[$1], $5, $3 - $5 + 1);
- free($1);
+ sigspec '[' TOK_INT ':' TOK_INT ']' {
+ $$ = new RTLIL::SigSpec($1->extract($5, $3 - $5 + 1));
+ delete $1;
} |
'{' sigspec_list '}' {
$$ = $2;
diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc
index 0a5bd84d..6e3cffac 100644
--- a/frontends/liberty/liberty.cc
+++ b/frontends/liberty/liberty.cc
@@ -36,7 +36,8 @@ static RTLIL::SigSpec parse_func_identifier(RTLIL::Module *module, const char *&
int id_len = 0;
while (('a' <= expr[id_len] && expr[id_len] <= 'z') || ('A' <= expr[id_len] && expr[id_len] <= 'Z') ||
- ('0' <= expr[id_len] && expr[id_len] <= '9') || expr[id_len] == '.' || expr[id_len] == '_') id_len++;
+ ('0' <= expr[id_len] && expr[id_len] <= '9') || expr[id_len] == '.' ||
+ expr[id_len] == '_' || expr[id_len] == '[' || expr[id_len] == ']') id_len++;
if (id_len == 0)
log_error("Expected identifier at `%s'.\n", expr);
@@ -615,7 +616,7 @@ struct LibertyFrontend : public Frontend {
LibertyAst *bus_type_node = node->find("bus_type");
if (!bus_type_node || !type_map.count(bus_type_node->value))
- log_error("Unkown or unsupported type for bus interface %s on cell %s.\n",
+ log_error("Unknown or unsupported type for bus interface %s on cell %s.\n",
node->args.at(0).c_str(), log_id(cell_name));
int bus_type_width = std::get<0>(type_map.at(bus_type_node->value));
@@ -634,9 +635,12 @@ struct LibertyFrontend : public Frontend {
}
}
- for (auto node : cell->children)
+ if (!flag_lib)
{
- if (!flag_lib) {
+ // some liberty files do not put ff/latch at the beginning of a cell
+ // try to find "ff" or "latch" and create FF/latch _before_ processing all other nodes
+ for (auto node : cell->children)
+ {
if (node->id == "ff" && node->args.size() == 2)
create_ff(module, node);
if (node->id == "latch" && node->args.size() == 2)
@@ -645,7 +649,10 @@ struct LibertyFrontend : public Frontend {
goto skip_cell;
}
}
+ }
+ for (auto node : cell->children)
+ {
if (node->id == "pin" && node->args.size() == 1)
{
LibertyAst *dir = node->find("direction");
diff --git a/frontends/verific/README b/frontends/verific/README
index b4c436a3..89584f2e 100644
--- a/frontends/verific/README
+++ b/frontends/verific/README
@@ -4,35 +4,6 @@ This directory contains Verific bindings for Yosys.
See http://www.verific.com/ for details.
-Building Yosys with the 32 bit Verific eval library on amd64:
-=============================================================
-
-1.) Use a Makefile.conf like the following one:
-
---snip--
-CONFIG := gcc
-ENABLE_TCL := 0
-ENABLE_PLUGINS := 0
-ENABLE_VERIFIC := 1
-CXXFLAGS += -m32
-LDFLAGS += -m32
-VERIFIC_DIR = /usr/local/src/verific_lib_eval
---snap--
-
-
-2.) Install the necessary multilib packages
-
-Hint: On debian/ubuntu the multilib packages have names such as
-libreadline-dev:i386 or lib32readline6-dev, depending on the
-exact version of debian/ubuntu you are working with.
-
-
-3.) Build and test
-
-make -j8
-./yosys -p 'verific -sv frontends/verific/example.sv; verific -import top'
-
-
Verific Features that should be enabled in your Verific library
===============================================================
@@ -50,7 +21,7 @@ Then run in the following command in this directory:
sby -f example.sby
-This will generate approximately one page of text outpout. The last lines
+This will generate approximately one page of text output. The last lines
should be something like this:
SBY [example] summary: Elapsed clock time [H:MM:SS (secs)]: 0:00:00 (0)
diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc
index c5fa5831..ed9727b8 100644
--- a/frontends/verific/verific.cc
+++ b/frontends/verific/verific.cc
@@ -118,6 +118,27 @@ RTLIL::SigBit VerificImporter::net_map_at(Net *net)
return net_map.at(net);
}
+bool is_blackbox(Netlist *nl)
+{
+ if (nl->IsBlackBox())
+ return true;
+
+ const char *attr = nl->GetAttValue("blackbox");
+ if (attr != nullptr && strcmp(attr, "0"))
+ return true;
+
+ return false;
+}
+
+RTLIL::IdString VerificImporter::new_verific_id(Verific::DesignObj *obj)
+{
+ std::string s = stringf("$verific$%s", obj->Name());
+ if (obj->Linefile())
+ s += stringf("$%s:%d", Verific::LineFile::GetFileName(obj->Linefile()), Verific::LineFile::GetLineNo(obj->Linefile()));
+ s += stringf("$%d", autoidx++);
+ return s;
+}
+
void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, DesignObj *obj)
{
MapIter mi;
@@ -203,7 +224,7 @@ RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool<Net*,
dummy_wire = NULL;
} else {
if (dummy_wire == NULL)
- dummy_wire = module->addWire(NEW_ID);
+ dummy_wire = module->addWire(new_verific_id(inst));
else
dummy_wire->width++;
sig.append(RTLIL::SigSpec(dummy_wire, dummy_wire->width - 1));
@@ -219,8 +240,8 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr
}
if (inst->Type() == PRIM_NAND) {
- RTLIL::SigSpec tmp = module->addWire(NEW_ID);
- module->addAndGate(NEW_ID, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp);
+ RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst));
+ module->addAndGate(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp);
module->addNotGate(inst_name, tmp, net_map_at(inst->GetOutput()));
return true;
}
@@ -231,8 +252,8 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr
}
if (inst->Type() == PRIM_NOR) {
- RTLIL::SigSpec tmp = module->addWire(NEW_ID);
- module->addOrGate(NEW_ID, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp);
+ RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst));
+ module->addOrGate(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp);
module->addNotGate(inst_name, tmp, net_map_at(inst->GetOutput()));
return true;
}
@@ -272,16 +293,16 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr
if (inst->Type() == PRIM_FADD)
{
RTLIL::SigSpec a = net_map_at(inst->GetInput1()), b = net_map_at(inst->GetInput2()), c = net_map_at(inst->GetCin());
- RTLIL::SigSpec x = inst->GetCout() ? net_map_at(inst->GetCout()) : module->addWire(NEW_ID);
- RTLIL::SigSpec y = inst->GetOutput() ? net_map_at(inst->GetOutput()) : module->addWire(NEW_ID);
- RTLIL::SigSpec tmp1 = module->addWire(NEW_ID);
- RTLIL::SigSpec tmp2 = module->addWire(NEW_ID);
- RTLIL::SigSpec tmp3 = module->addWire(NEW_ID);
- module->addXorGate(NEW_ID, a, b, tmp1);
+ RTLIL::SigSpec x = inst->GetCout() ? net_map_at(inst->GetCout()) : module->addWire(new_verific_id(inst));
+ RTLIL::SigSpec y = inst->GetOutput() ? net_map_at(inst->GetOutput()) : module->addWire(new_verific_id(inst));
+ RTLIL::SigSpec tmp1 = module->addWire(new_verific_id(inst));
+ RTLIL::SigSpec tmp2 = module->addWire(new_verific_id(inst));
+ RTLIL::SigSpec tmp3 = module->addWire(new_verific_id(inst));
+ module->addXorGate(new_verific_id(inst), a, b, tmp1);
module->addXorGate(inst_name, tmp1, c, y);
- module->addAndGate(NEW_ID, tmp1, c, tmp2);
- module->addAndGate(NEW_ID, a, b, tmp3);
- module->addOrGate(NEW_ID, tmp2, tmp3, x);
+ module->addAndGate(new_verific_id(inst), tmp1, c, tmp2);
+ module->addAndGate(new_verific_id(inst), a, b, tmp3);
+ module->addOrGate(new_verific_id(inst), tmp2, tmp3, x);
return true;
}
@@ -308,63 +329,78 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr
bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdString inst_name)
{
+ RTLIL::Cell *cell = nullptr;
+
if (inst->Type() == PRIM_AND) {
- module->addAnd(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
+ cell = module->addAnd(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == PRIM_NAND) {
- RTLIL::SigSpec tmp = module->addWire(NEW_ID);
- module->addAnd(NEW_ID, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp);
- module->addNot(inst_name, tmp, net_map_at(inst->GetOutput()));
+ RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst));
+ cell = module->addAnd(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp);
+ import_attributes(cell->attributes, inst);
+ cell = module->addNot(inst_name, tmp, net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == PRIM_OR) {
- module->addOr(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
+ cell = module->addOr(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == PRIM_NOR) {
- RTLIL::SigSpec tmp = module->addWire(NEW_ID);
- module->addOr(NEW_ID, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp);
- module->addNot(inst_name, tmp, net_map_at(inst->GetOutput()));
+ RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst));
+ cell = module->addOr(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp);
+ import_attributes(cell->attributes, inst);
+ cell = module->addNot(inst_name, tmp, net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == PRIM_XOR) {
- module->addXor(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
+ cell = module->addXor(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == PRIM_XNOR) {
- module->addXnor(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
+ cell = module->addXnor(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == PRIM_INV) {
- module->addNot(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ cell = module->addNot(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == PRIM_MUX) {
- module->addMux(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput()));
+ cell = module->addMux(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == PRIM_TRI) {
- module->addMux(inst_name, RTLIL::State::Sz, net_map_at(inst->GetInput()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput()));
+ cell = module->addMux(inst_name, RTLIL::State::Sz, net_map_at(inst->GetInput()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == PRIM_FADD)
{
- RTLIL::SigSpec a_plus_b = module->addWire(NEW_ID, 2);
- RTLIL::SigSpec y = inst->GetOutput() ? net_map_at(inst->GetOutput()) : module->addWire(NEW_ID);
+ RTLIL::SigSpec a_plus_b = module->addWire(new_verific_id(inst), 2);
+ RTLIL::SigSpec y = inst->GetOutput() ? net_map_at(inst->GetOutput()) : module->addWire(new_verific_id(inst));
if (inst->GetCout())
y.append(net_map_at(inst->GetCout()));
- module->addAdd(NEW_ID, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), a_plus_b);
- module->addAdd(inst_name, a_plus_b, net_map_at(inst->GetCin()), y);
+ cell = module->addAdd(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), a_plus_b);
+ import_attributes(cell->attributes, inst);
+ cell = module->addAdd(inst_name, a_plus_b, net_map_at(inst->GetCin()), y);
+ import_attributes(cell->attributes, inst);
return true;
}
@@ -375,24 +411,26 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
log_assert(clocking.body_net == nullptr);
if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd())
- clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ cell = clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
else if (inst->GetSet()->IsGnd())
- clocking.addAdff(inst_name, net_map_at(inst->GetReset()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), RTLIL::State::S0);
+ cell = clocking.addAdff(inst_name, net_map_at(inst->GetReset()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), RTLIL::State::S0);
else if (inst->GetReset()->IsGnd())
- clocking.addAdff(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), RTLIL::State::S1);
+ cell = clocking.addAdff(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), RTLIL::State::S1);
else
- clocking.addDffsr(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetReset()),
+ cell = clocking.addDffsr(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetReset()),
net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == PRIM_DLATCHRS)
{
if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd())
- module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
else
- module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetSet()), net_map_at(inst->GetReset()),
+ cell = module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetSet()), net_map_at(inst->GetReset()),
net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
return true;
}
@@ -408,37 +446,45 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
if (inst->GetCout() != NULL)
out.append(net_map_at(inst->GetCout()));
if (inst->GetCin()->IsGnd()) {
- module->addAdd(inst_name, IN1, IN2, out, SIGNED);
+ cell = module->addAdd(inst_name, IN1, IN2, out, SIGNED);
+ import_attributes(cell->attributes, inst);
} else {
- RTLIL::SigSpec tmp = module->addWire(NEW_ID, GetSize(out));
- module->addAdd(NEW_ID, IN1, IN2, tmp, SIGNED);
- module->addAdd(inst_name, tmp, net_map_at(inst->GetCin()), out, false);
+ RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst), GetSize(out));
+ cell = module->addAdd(new_verific_id(inst), IN1, IN2, tmp, SIGNED);
+ import_attributes(cell->attributes, inst);
+ cell = module->addAdd(inst_name, tmp, net_map_at(inst->GetCin()), out, false);
+ import_attributes(cell->attributes, inst);
}
return true;
}
if (inst->Type() == OPER_MULTIPLIER) {
- module->addMul(inst_name, IN1, IN2, OUT, SIGNED);
+ cell = module->addMul(inst_name, IN1, IN2, OUT, SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_DIVIDER) {
- module->addDiv(inst_name, IN1, IN2, OUT, SIGNED);
+ cell = module->addDiv(inst_name, IN1, IN2, OUT, SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_MODULO) {
- module->addMod(inst_name, IN1, IN2, OUT, SIGNED);
+ cell = module->addMod(inst_name, IN1, IN2, OUT, SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_REMAINDER) {
- module->addMod(inst_name, IN1, IN2, OUT, SIGNED);
+ cell = module->addMod(inst_name, IN1, IN2, OUT, SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_SHIFT_LEFT) {
- module->addShl(inst_name, IN1, IN2, OUT, false);
+ cell = module->addShl(inst_name, IN1, IN2, OUT, false);
+ import_attributes(cell->attributes, inst);
return true;
}
@@ -448,7 +494,8 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
for (unsigned i = 1; i < inst->OutputSize(); i++) {
vec.append(RTLIL::State::S0);
}
- module->addShl(inst_name, vec, IN, OUT, false);
+ cell = module->addShl(inst_name, vec, IN, OUT, false);
+ import_attributes(cell->attributes, inst);
return true;
}
@@ -458,7 +505,8 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
for (unsigned i = 1; i < inst->OutputSize(); i++) {
vec.append(RTLIL::State::S0);
}
- module->addShl(inst_name, vec, IN, OUT, false);
+ cell = module->addShl(inst_name, vec, IN, OUT, false);
+ import_attributes(cell->attributes, inst);
return true;
}
@@ -466,108 +514,127 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
Net *net_cin = inst->GetCin();
Net *net_a_msb = inst->GetInput1Bit(0);
if (net_cin->IsGnd())
- module->addShr(inst_name, IN1, IN2, OUT, false);
+ cell = module->addShr(inst_name, IN1, IN2, OUT, false);
else if (net_cin == net_a_msb)
- module->addSshr(inst_name, IN1, IN2, OUT, true);
+ cell = module->addSshr(inst_name, IN1, IN2, OUT, true);
else
log_error("Can't import Verific OPER_SHIFT_RIGHT instance %s: carry_in is neither 0 nor msb of left input\n", inst->Name());
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_REDUCE_AND) {
- module->addReduceAnd(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
+ cell = module->addReduceAnd(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_REDUCE_OR) {
- module->addReduceOr(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
+ cell = module->addReduceOr(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_REDUCE_XOR) {
- module->addReduceXor(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
+ cell = module->addReduceXor(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_REDUCE_XNOR) {
- module->addReduceXnor(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
+ cell = module->addReduceXnor(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_REDUCE_NOR) {
- SigSpec t = module->ReduceOr(NEW_ID, IN, SIGNED);
- module->addNot(inst_name, t, net_map_at(inst->GetOutput()));
+ SigSpec t = module->ReduceOr(new_verific_id(inst), IN, SIGNED);
+ cell = module->addNot(inst_name, t, net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_LESSTHAN) {
Net *net_cin = inst->GetCin();
if (net_cin->IsGnd())
- module->addLt(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED);
+ cell = module->addLt(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED);
else if (net_cin->IsPwr())
- module->addLe(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED);
+ cell = module->addLe(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED);
else
log_error("Can't import Verific OPER_LESSTHAN instance %s: carry_in is neither 0 nor 1\n", inst->Name());
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_WIDE_AND) {
- module->addAnd(inst_name, IN1, IN2, OUT, SIGNED);
+ cell = module->addAnd(inst_name, IN1, IN2, OUT, SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_WIDE_OR) {
- module->addOr(inst_name, IN1, IN2, OUT, SIGNED);
+ cell = module->addOr(inst_name, IN1, IN2, OUT, SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_WIDE_XOR) {
- module->addXor(inst_name, IN1, IN2, OUT, SIGNED);
+ cell = module->addXor(inst_name, IN1, IN2, OUT, SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_WIDE_XNOR) {
- module->addXnor(inst_name, IN1, IN2, OUT, SIGNED);
+ cell = module->addXnor(inst_name, IN1, IN2, OUT, SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_WIDE_BUF) {
- module->addPos(inst_name, IN, FILTERED_OUT, SIGNED);
+ cell = module->addPos(inst_name, IN, FILTERED_OUT, SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_WIDE_INV) {
- module->addNot(inst_name, IN, OUT, SIGNED);
+ cell = module->addNot(inst_name, IN, OUT, SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_MINUS) {
- module->addSub(inst_name, IN1, IN2, OUT, SIGNED);
+ cell = module->addSub(inst_name, IN1, IN2, OUT, SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_UMINUS) {
- module->addNeg(inst_name, IN, OUT, SIGNED);
+ cell = module->addNeg(inst_name, IN, OUT, SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_EQUAL) {
- module->addEq(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED);
+ cell = module->addEq(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_NEQUAL) {
- module->addNe(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED);
+ cell = module->addNe(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_WIDE_MUX) {
- module->addMux(inst_name, IN1, IN2, net_map_at(inst->GetControl()), OUT);
+ cell = module->addMux(inst_name, IN1, IN2, net_map_at(inst->GetControl()), OUT);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_NTO1MUX) {
- module->addShr(inst_name, IN2, IN1, net_map_at(inst->GetOutput()));
+ cell = module->addShr(inst_name, IN2, IN1, net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
return true;
}
@@ -587,25 +654,29 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
padded_data.append(d);
}
- module->addShr(inst_name, padded_data, sel, out);
+ cell = module->addShr(inst_name, padded_data, sel, out);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_SELECTOR)
{
- module->addPmux(inst_name, State::S0, IN2, IN1, net_map_at(inst->GetOutput()));
+ cell = module->addPmux(inst_name, State::S0, IN2, IN1, net_map_at(inst->GetOutput()));
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_WIDE_SELECTOR)
{
SigSpec out = OUT;
- module->addPmux(inst_name, SigSpec(State::S0, GetSize(out)), IN2, IN1, out);
+ cell = module->addPmux(inst_name, SigSpec(State::S0, GetSize(out)), IN2, IN1, out);
+ import_attributes(cell->attributes, inst);
return true;
}
if (inst->Type() == OPER_WIDE_TRI) {
- module->addMux(inst_name, RTLIL::SigSpec(RTLIL::State::Sz, inst->OutputSize()), IN, net_map_at(inst->GetControl()), OUT);
+ cell = module->addMux(inst_name, RTLIL::SigSpec(RTLIL::State::Sz, inst->OutputSize()), IN, net_map_at(inst->GetControl()), OUT);
+ import_attributes(cell->attributes, inst);
return true;
}
@@ -619,9 +690,10 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr
RTLIL::SigSpec sig_reset = operatorInport(inst, "reset");
if (sig_set.is_fully_const() && !sig_set.as_bool() && sig_reset.is_fully_const() && !sig_reset.as_bool())
- clocking.addDff(inst_name, IN, OUT);
+ cell = clocking.addDff(inst_name, IN, OUT);
else
- clocking.addDffsr(inst_name, sig_set, sig_reset, IN, OUT);
+ cell = clocking.addDffsr(inst_name, sig_set, sig_reset, IN, OUT);
+ import_attributes(cell->attributes, inst);
return true;
}
@@ -709,7 +781,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
netlist = nl;
if (design->has(module_name)) {
- if (!nl->IsOperator())
+ if (!nl->IsOperator() && !is_blackbox(nl))
log_cmd_error("Re-definition of module `%s'.\n", nl->Owner()->Name());
return;
}
@@ -718,7 +790,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
module->name = module_name;
design->add(module);
- if (nl->IsBlackBox()) {
+ if (is_blackbox(nl)) {
log("Importing blackbox module %s.\n", RTLIL::id2cstr(module->name));
module->set_bool_attribute("\\blackbox");
} else {
@@ -850,7 +922,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
ascii_initdata++;
}
if (initval_valid) {
- RTLIL::Cell *cell = module->addCell(NEW_ID, "$meminit");
+ RTLIL::Cell *cell = module->addCell(new_verific_id(net), "$meminit");
cell->parameters["\\WORDS"] = 1;
if (net->GetOrigTypeRange()->LeftRangeBound() < net->GetOrigTypeRange()->RightRangeBound())
cell->setPort("\\ADDR", word_idx);
@@ -913,7 +985,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
if (net->Bus())
continue;
- RTLIL::IdString wire_name = module->uniquify(mode_names || net->IsUserDeclared() ? RTLIL::escape_id(net->Name()) : NEW_ID);
+ RTLIL::IdString wire_name = module->uniquify(mode_names || net->IsUserDeclared() ? RTLIL::escape_id(net->Name()) : new_verific_id(net));
if (verific_verbose)
log(" importing net %s as %s.\n", net->Name(), log_id(wire_name));
@@ -937,7 +1009,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
if (found_new_net)
{
- RTLIL::IdString wire_name = module->uniquify(mode_names || netbus->IsUserDeclared() ? RTLIL::escape_id(netbus->Name()) : NEW_ID);
+ RTLIL::IdString wire_name = module->uniquify(mode_names || netbus->IsUserDeclared() ? RTLIL::escape_id(netbus->Name()) : new_verific_id(netbus));
if (verific_verbose)
log(" importing netbus %s as %s.\n", netbus->Name(), log_id(wire_name));
@@ -1013,16 +1085,16 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
}
if (GetSize(anyconst_sig))
- module->connect(anyconst_sig, module->Anyconst(NEW_ID, GetSize(anyconst_sig)));
+ module->connect(anyconst_sig, module->Anyconst(new_verific_id(netbus), GetSize(anyconst_sig)));
if (GetSize(anyseq_sig))
- module->connect(anyseq_sig, module->Anyseq(NEW_ID, GetSize(anyseq_sig)));
+ module->connect(anyseq_sig, module->Anyseq(new_verific_id(netbus), GetSize(anyseq_sig)));
if (GetSize(allconst_sig))
- module->connect(allconst_sig, module->Allconst(NEW_ID, GetSize(allconst_sig)));
+ module->connect(allconst_sig, module->Allconst(new_verific_id(netbus), GetSize(allconst_sig)));
if (GetSize(allseq_sig))
- module->connect(allseq_sig, module->Allseq(NEW_ID, GetSize(allseq_sig)));
+ module->connect(allseq_sig, module->Allseq(new_verific_id(netbus), GetSize(allseq_sig)));
}
for (auto it : init_nets)
@@ -1046,10 +1118,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
}
for (auto net : anyconst_nets)
- module->connect(net_map_at(net), module->Anyconst(NEW_ID));
+ module->connect(net_map_at(net), module->Anyconst(new_verific_id(net)));
for (auto net : anyseq_nets)
- module->connect(net_map_at(net), module->Anyseq(NEW_ID));
+ module->connect(net_map_at(net), module->Anyseq(new_verific_id(net)));
pool<Instance*, hash_ptr_ops> sva_asserts;
pool<Instance*, hash_ptr_ops> sva_assumes;
@@ -1060,7 +1132,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
FOREACH_INSTANCE_OF_NETLIST(nl, mi, inst)
{
- RTLIL::IdString inst_name = module->uniquify(mode_names || inst->IsUserDeclared() ? RTLIL::escape_id(inst->Name()) : NEW_ID);
+ RTLIL::IdString inst_name = module->uniquify(mode_names || inst->IsUserDeclared() ? RTLIL::escape_id(inst->Name()) : new_verific_id(inst));
if (verific_verbose)
log(" importing cell %s (%s) as %s.\n", inst->Name(), inst->View()->Owner()->Name(), log_id(inst_name));
@@ -1128,27 +1200,34 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
if (inst->Type() == OPER_WRITE_PORT || inst->Type() == OPER_CLOCKED_WRITE_PORT)
{
RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetOutput()->Name()));
- if (memory->width != int(inst->Input2Size()))
- log_error("Import of asymmetric memories of this type is not supported yet: %s %s\n", inst->Name(), inst->GetInput()->Name());
+ int numchunks = int(inst->Input2Size()) / memory->width;
+ int chunksbits = ceil_log2(numchunks);
+
+ if ((numchunks * memory->width) != int(inst->Input2Size()) || (numchunks & (numchunks - 1)) != 0)
+ log_error("Import of asymmetric memories of this type is not supported yet: %s %s\n", inst->Name(), inst->GetOutput()->Name());
+
+ for (int i = 0; i < numchunks; i++)
+ {
+ RTLIL::SigSpec addr = {operatorInput1(inst), RTLIL::Const(i, chunksbits)};
+ RTLIL::SigSpec data = operatorInput2(inst).extract(i * memory->width, memory->width);
+
+ RTLIL::Cell *cell = module->addCell(numchunks == 1 ? inst_name :
+ RTLIL::IdString(stringf("%s_%d", inst_name.c_str(), i)), "$memwr");
+ cell->parameters["\\MEMID"] = memory->name.str();
+ cell->parameters["\\CLK_ENABLE"] = false;
+ cell->parameters["\\CLK_POLARITY"] = true;
+ cell->parameters["\\PRIORITY"] = 0;
+ cell->parameters["\\ABITS"] = GetSize(addr);
+ cell->parameters["\\WIDTH"] = GetSize(data);
+ cell->setPort("\\EN", RTLIL::SigSpec(net_map_at(inst->GetControl())).repeat(GetSize(data)));
+ cell->setPort("\\CLK", RTLIL::State::S0);
+ cell->setPort("\\ADDR", addr);
+ cell->setPort("\\DATA", data);
- RTLIL::SigSpec addr = operatorInput1(inst);
- RTLIL::SigSpec data = operatorInput2(inst);
-
- RTLIL::Cell *cell = module->addCell(inst_name, "$memwr");
- cell->parameters["\\MEMID"] = memory->name.str();
- cell->parameters["\\CLK_ENABLE"] = false;
- cell->parameters["\\CLK_POLARITY"] = true;
- cell->parameters["\\PRIORITY"] = 0;
- cell->parameters["\\ABITS"] = GetSize(addr);
- cell->parameters["\\WIDTH"] = GetSize(data);
- cell->setPort("\\EN", RTLIL::SigSpec(net_map_at(inst->GetControl())).repeat(GetSize(data)));
- cell->setPort("\\CLK", RTLIL::State::S0);
- cell->setPort("\\ADDR", addr);
- cell->setPort("\\DATA", data);
-
- if (inst->Type() == OPER_CLOCKED_WRITE_PORT) {
- cell->parameters["\\CLK_ENABLE"] = true;
- cell->setPort("\\CLK", net_map_at(inst->GetClock()));
+ if (inst->Type() == OPER_CLOCKED_WRITE_PORT) {
+ cell->parameters["\\CLK_ENABLE"] = true;
+ cell->setPort("\\CLK", net_map_at(inst->GetClock()));
+ }
}
continue;
}
@@ -1184,7 +1263,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
log_assert(inst->Input1Size() == inst->OutputSize());
SigSpec sig_d, sig_q, sig_o;
- sig_q = module->addWire(NEW_ID, inst->Input1Size());
+ sig_q = module->addWire(new_verific_id(inst), inst->Input1Size());
for (int i = int(inst->Input1Size())-1; i >= 0; i--){
sig_d.append(net_map_at(inst->GetInput1Bit(i)));
@@ -1198,8 +1277,8 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
log_signal(sig_d), log_signal(sig_q), log_signal(sig_o));
}
- clocking.addDff(NEW_ID, sig_d, sig_q);
- module->addXnor(NEW_ID, sig_d, sig_q, sig_o);
+ clocking.addDff(new_verific_id(inst), sig_d, sig_q);
+ module->addXnor(new_verific_id(inst), sig_d, sig_q, sig_o);
if (!mode_keep)
continue;
@@ -1213,7 +1292,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
SigSpec sig_d = net_map_at(inst->GetInput1());
SigSpec sig_o = net_map_at(inst->GetOutput());
- SigSpec sig_q = module->addWire(NEW_ID);
+ SigSpec sig_q = module->addWire(new_verific_id(inst));
if (verific_verbose) {
log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg",
@@ -1222,8 +1301,8 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
log_signal(sig_d), log_signal(sig_q), log_signal(sig_o));
}
- clocking.addDff(NEW_ID, sig_d, sig_q);
- module->addXnor(NEW_ID, sig_d, sig_q, sig_o);
+ clocking.addDff(new_verific_id(inst), sig_d, sig_q);
+ module->addXnor(new_verific_id(inst), sig_d, sig_q, sig_o);
if (!mode_keep)
continue;
@@ -1242,7 +1321,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg",
log_signal(sig_d), log_signal(sig_q), log_signal(clocking.clock_sig));
- past_ffs.insert(clocking.addDff(NEW_ID, sig_d, sig_q));
+ past_ffs.insert(clocking.addDff(new_verific_id(inst), sig_d, sig_q));
if (!mode_keep)
continue;
@@ -1256,14 +1335,14 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
SigBit sig_d = net_map_at(inst->GetInput1());
SigBit sig_o = net_map_at(inst->GetOutput());
- SigBit sig_q = module->addWire(NEW_ID);
+ SigBit sig_q = module->addWire(new_verific_id(inst));
if (verific_verbose)
log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg",
log_signal(sig_d), log_signal(sig_q), log_signal(clocking.clock_sig));
- clocking.addDff(NEW_ID, sig_d, sig_q);
- module->addEq(NEW_ID, {sig_q, sig_d}, Const(inst->Type() == PRIM_SVA_ROSE ? 1 : 2, 2), sig_o);
+ clocking.addDff(new_verific_id(inst), sig_d, sig_q);
+ module->addEq(new_verific_id(inst), {sig_q, sig_d}, Const(inst->Type() == PRIM_SVA_ROSE ? 1 : 2, 2), sig_o);
if (!mode_keep)
continue;
@@ -1286,9 +1365,9 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
Cell *cell = nullptr;
if (assume_attr != nullptr && !strcmp(assume_attr, "1"))
- cell = module->addAssume(NEW_ID, cond, State::S1);
+ cell = module->addAssume(new_verific_id(inst), cond, State::S1);
else
- cell = module->addAssert(NEW_ID, cond, State::S1);
+ cell = module->addAssert(new_verific_id(inst), cond, State::S1);
import_attributes(cell->attributes, inst);
continue;
@@ -1330,7 +1409,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
IdString port_name_id = RTLIL::escape_id(port_name);
auto &sigvec = cell_port_conns[port_name_id];
if (GetSize(sigvec) <= port_offset) {
- SigSpec zwires = module->addWire(NEW_ID, port_offset+1-GetSize(sigvec));
+ SigSpec zwires = module->addWire(new_verific_id(inst), port_offset+1-GetSize(sigvec));
for (auto bit : zwires)
sigvec.push_back(bit);
}
@@ -1540,30 +1619,35 @@ struct VerificExtNets
int portname_cnt = 0;
// a map from Net to the same Net one level up in the design hierarchy
- std::map<Net*, Net*> net_level_up;
+ std::map<Net*, Net*> net_level_up_drive_up;
+ std::map<Net*, Net*> net_level_up_drive_down;
- Net *get_net_level_up(Net *net)
+ Net *route_up(Net *net, bool drive_up, Net *final_net = nullptr)
{
+ auto &net_level_up = drive_up ? net_level_up_drive_up : net_level_up_drive_down;
+
if (net_level_up.count(net) == 0)
{
Netlist *nl = net->Owner();
// Simply return if Netlist is not unique
- if (nl->NumOfRefs() != 1)
- return net;
+ log_assert(nl->NumOfRefs() == 1);
Instance *up_inst = (Instance*)nl->GetReferences()->GetLast();
Netlist *up_nl = up_inst->Owner();
// create new Port
string name = stringf("___extnets_%d", portname_cnt++);
- Port *new_port = new Port(name.c_str(), DIR_OUT);
+ Port *new_port = new Port(name.c_str(), drive_up ? DIR_OUT : DIR_IN);
nl->Add(new_port);
net->Connect(new_port);
// create new Net in up Netlist
- Net *new_net = new Net(name.c_str());
- up_nl->Add(new_net);
+ Net *new_net = final_net;
+ if (new_net == nullptr || new_net->Owner() != up_nl) {
+ new_net = new Net(name.c_str());
+ up_nl->Add(new_net);
+ }
up_inst->Connect(new_port, new_net);
net_level_up[net] = new_net;
@@ -1572,6 +1656,39 @@ struct VerificExtNets
return net_level_up.at(net);
}
+ Net *route_up(Net *net, bool drive_up, Netlist *dest, Net *final_net = nullptr)
+ {
+ while (net->Owner() != dest)
+ net = route_up(net, drive_up, final_net);
+ if (final_net != nullptr)
+ log_assert(net == final_net);
+ return net;
+ }
+
+ Netlist *find_common_ancestor(Netlist *A, Netlist *B)
+ {
+ std::set<Netlist*> ancestors_of_A;
+
+ Netlist *cursor = A;
+ while (1) {
+ ancestors_of_A.insert(cursor);
+ if (cursor->NumOfRefs() != 1)
+ break;
+ cursor = ((Instance*)cursor->GetReferences()->GetLast())->Owner();
+ }
+
+ cursor = B;
+ while (1) {
+ if (ancestors_of_A.count(cursor))
+ return cursor;
+ if (cursor->NumOfRefs() != 1)
+ break;
+ cursor = ((Instance*)cursor->GetReferences()->GetLast())->Owner();
+ }
+
+ log_error("No common ancestor found between %s and %s.\n", get_full_netlist_name(A).c_str(), get_full_netlist_name(B).c_str());
+ }
+
void run(Netlist *nl)
{
MapIter mi, mi2;
@@ -1595,19 +1712,37 @@ struct VerificExtNets
if (verific_verbose)
log("Fixing external net reference on port %s.%s.%s:\n", get_full_netlist_name(nl).c_str(), inst->Name(), port->Name());
- while (net->IsExternalTo(nl))
- {
- Net *newnet = get_net_level_up(net);
- if (newnet == net) break;
+ Netlist *ext_nl = net->Owner();
+
+ if (verific_verbose)
+ log(" external net owner: %s\n", get_full_netlist_name(ext_nl).c_str());
+
+ Netlist *ca_nl = find_common_ancestor(nl, ext_nl);
+
+ if (verific_verbose)
+ log(" common ancestor: %s\n", get_full_netlist_name(ca_nl).c_str());
+ Net *ca_net = route_up(net, !port->IsOutput(), ca_nl);
+ Net *new_net = ca_net;
+
+ if (ca_nl != nl)
+ {
if (verific_verbose)
- log(" external net: %s.%s\n", get_full_netlist_name(net->Owner()).c_str(), net->Name());
- net = newnet;
+ log(" net in common ancestor: %s\n", ca_net->Name());
+
+ string name = stringf("___extnets_%d", portname_cnt++);
+ new_net = new Net(name.c_str());
+ nl->Add(new_net);
+
+ Net *n = route_up(new_net, port->IsOutput(), ca_nl, ca_net);
+ log_assert(n == ca_net);
}
if (verific_verbose)
- log(" final net: %s.%s%s\n", get_full_netlist_name(net->Owner()).c_str(), net->Name(), net->IsExternalTo(nl) ? " (external)" : "");
- todo_connect.push_back(tuple<Instance*, Port*, Net*>(inst, port, net));
+ log(" new local net: %s\n", new_net->Name());
+
+ log_assert(!new_net->IsExternalTo(nl));
+ todo_connect.push_back(tuple<Instance*, Port*, Net*>(inst, port, new_net));
}
for (auto it : todo_connect) {
@@ -1676,6 +1811,7 @@ YOSYS_NAMESPACE_END
PRIVATE_NAMESPACE_BEGIN
+#ifdef YOSYS_ENABLE_VERIFIC
bool check_noverific_env()
{
const char *e = getenv("YOSYS_NOVERIFIC");
@@ -1685,6 +1821,7 @@ bool check_noverific_env()
return false;
return true;
}
+#endif
struct VerificPass : public Pass {
VerificPass() : Pass("verific", "load Verilog and VHDL designs using Verific") { }
@@ -1774,6 +1911,13 @@ struct VerificPass : public Pass {
log(" -autocover\n");
log(" Generate automatic cover statements for all asserts\n");
log("\n");
+ log(" -chparam name value \n");
+ log(" Elaborate the specified top modules (all modules when -all given) using\n");
+ log(" this parameter value. Modules on which this parameter does not exist will\n");
+ log(" cause Verific to produce a VERI-1928 or VHDL-1676 message. This option\n");
+ log(" can be specified multiple times to override multiple parameters.\n");
+ log(" String values must be passed in double quotes (\").\n");
+ log("\n");
log(" -v, -vv\n");
log(" Verbose log messages. (-vv is even more verbose than -v.)\n");
log("\n");
@@ -1819,12 +1963,19 @@ struct VerificPass : public Pass {
{
Message::SetConsoleOutput(0);
Message::RegisterCallBackMsg(msg_func);
+
RuntimeFlags::SetVar("db_preserve_user_nets", 1);
RuntimeFlags::SetVar("db_allow_external_nets", 1);
- RuntimeFlags::SetVar("vhdl_ignore_assertion_statements", 0);
+ RuntimeFlags::SetVar("db_infer_wide_operators", 1);
+
RuntimeFlags::SetVar("veri_extract_dualport_rams", 0);
RuntimeFlags::SetVar("veri_extract_multiport_rams", 1);
- RuntimeFlags::SetVar("db_infer_wide_operators", 1);
+
+ RuntimeFlags::SetVar("vhdl_extract_dualport_rams", 0);
+ RuntimeFlags::SetVar("vhdl_extract_multiport_rams", 1);
+
+ RuntimeFlags::SetVar("vhdl_support_variable_slice", 1);
+ RuntimeFlags::SetVar("vhdl_ignore_assertion_statements", 0);
// Workaround for VIPER #13851
RuntimeFlags::SetVar("veri_create_name_for_unnamed_gen_block", 1);
@@ -1832,6 +1983,10 @@ struct VerificPass : public Pass {
// WARNING: instantiating unknown module 'XYZ' (VERI-1063)
Message::SetMessageType("VERI-1063", VERIFIC_ERROR);
+#ifndef DB_PRESERVE_INITIAL_VALUE
+# warning Verific was built without DB_PRESERVE_INITIAL_VALUE.
+#endif
+
set_verific_global_flags = false;
}
@@ -2017,6 +2172,7 @@ struct VerificPass : public Pass {
bool mode_autocover = false;
bool flatten = false, extnets = false;
string dumpfile;
+ Map parameters(STRING_HASH);
for (argidx++; argidx < GetSize(args); argidx++) {
if (args[argidx] == "-all") {
@@ -2055,6 +2211,15 @@ struct VerificPass : public Pass {
mode_autocover = true;
continue;
}
+ if (args[argidx] == "-chparam" && argidx+2 < GetSize(args)) {
+ 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)
+ log_warning_noprefix("-chparam %s already specified: overwriting.\n", key.c_str());
+ continue;
+ }
if (args[argidx] == "-V") {
mode_verific = true;
continue;
@@ -2079,42 +2244,6 @@ struct VerificPass : public Pass {
if (mode_all)
{
-#if 0
- log("Running veri_file::ElaborateAll().\n");
- if (!veri_file::ElaborateAll())
- log_cmd_error("Elaboration of Verilog modules failed.\n");
-
- log("Running vhdl_file::ElaborateAll().\n");
- if (!vhdl_file::ElaborateAll())
- log_cmd_error("Elaboration of VHDL modules failed.\n");
-
- Library *lib = Netlist::PresentDesign()->Owner()->Owner();
-
- if (argidx == GetSize(args))
- {
- MapIter iter;
- char *iter_name;
- Verific::Cell *iter_cell;
-
- FOREACH_MAP_ITEM(lib->GetCells(), iter, &iter_name, &iter_cell) {
- if (*iter_name != '$')
- nl_todo.insert(iter_cell->GetFirstNetlist());
- }
- }
- else
- {
- for (; argidx < GetSize(args); argidx++)
- {
- Verific::Cell *cell = lib->GetCell(args[argidx].c_str());
-
- if (cell == nullptr)
- log_cmd_error("Module not found: %s\n", args[argidx].c_str());
-
- nl_todo.insert(cell->GetFirstNetlist());
- cell->GetFirstNetlist()->SetPresentDesign();
- }
- }
-#else
log("Running hier_tree::ElaborateAll().\n");
VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1);
@@ -2124,35 +2253,19 @@ struct VerificPass : public Pass {
if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib);
if (veri_lib) veri_libs.InsertLast(veri_lib);
- Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs);
+ Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, &parameters);
Netlist *nl;
int i;
FOREACH_ARRAY_ITEM(netlists, i, nl)
nl_todo.insert(nl);
delete netlists;
-#endif
}
else
{
if (argidx == GetSize(args))
log_cmd_error("No top module specified.\n");
-#if 0
- for (; argidx < GetSize(args); argidx++) {
- if (veri_file::GetModule(args[argidx].c_str())) {
- log("Running veri_file::Elaborate(\"%s\").\n", args[argidx].c_str());
- if (!veri_file::Elaborate(args[argidx].c_str()))
- log_cmd_error("Elaboration of top module `%s' failed.\n", args[argidx].c_str());
- nl_todo.insert(Netlist::PresentDesign());
- } else {
- log("Running vhdl_file::Elaborate(\"%s\").\n", args[argidx].c_str());
- if (!vhdl_file::Elaborate(args[argidx].c_str()))
- log_cmd_error("Elaboration of top module `%s' failed.\n", args[argidx].c_str());
- nl_todo.insert(Netlist::PresentDesign());
- }
- }
-#else
Array veri_modules, vhdl_units;
for (; argidx < GetSize(args); argidx++)
{
@@ -2177,14 +2290,13 @@ struct VerificPass : public Pass {
}
log("Running hier_tree::Elaborate().\n");
- Array *netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units);
+ Array *netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, &parameters);
Netlist *nl;
int i;
FOREACH_ARRAY_ITEM(netlists, i, nl)
nl_todo.insert(nl);
delete netlists;
-#endif
}
if (!verific_error_msg.empty())
@@ -2274,21 +2386,43 @@ struct ReadPass : public Pass {
log("\n");
log("Add directory to global Verilog/SystemVerilog include directories.\n");
log("\n");
+ log("\n");
+ log(" read -verific\n");
+ log(" read -noverific\n");
+ log("\n");
+ log("Subsequent calls to 'read' will either use or not use Verific. Calling 'read'\n");
+ log("with -verific will result in an error on Yosys binaries that are built without\n");
+ log("Verific support. The default is to use Verific if it is available.\n");
+ log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
- if (args.size() < 2)
+#ifdef YOSYS_ENABLE_VERIFIC
+ static bool verific_available = !check_noverific_env();
+#else
+ static bool verific_available = false;
+#endif
+ static bool use_verific = verific_available;
+
+ if (args.size() < 2 || args[1][0] != '-')
log_cmd_error("Missing mode parameter.\n");
+ if (args[1] == "-verific" || args[1] == "-noverific") {
+ if (args.size() != 2)
+ log_cmd_error("Additional arguments to -verific/-noverific.\n");
+ if (args[1] == "-verific") {
+ if (!verific_available)
+ log_cmd_error("This version of Yosys is built without Verific support.\n");
+ use_verific = true;
+ } else {
+ use_verific = false;
+ }
+ return;
+ }
+
if (args.size() < 3)
log_cmd_error("Missing file name parameter.\n");
-#ifdef YOSYS_ENABLE_VERIFIC
- bool use_verific = !check_noverific_env();
-#else
- bool use_verific = false;
-#endif
-
if (args[1] == "-vlog95" || args[1] == "-vlog2k") {
if (use_verific) {
args[0] = "verific";
diff --git a/frontends/verific/verific.h b/frontends/verific/verific.h
index 334a436a..b331dd4b 100644
--- a/frontends/verific/verific.h
+++ b/frontends/verific/verific.h
@@ -78,6 +78,7 @@ struct VerificImporter
RTLIL::SigBit net_map_at(Verific::Net *net);
+ RTLIL::IdString new_verific_id(Verific::DesignObj *obj);
void import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, Verific::DesignObj *obj);
RTLIL::SigSpec operatorInput(Verific::Instance *inst);
diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc
index cdc9ece8..8ea8372d 100644
--- a/frontends/verific/verificsva.cc
+++ b/frontends/verific/verificsva.cc
@@ -827,9 +827,9 @@ struct SvaFsm
for (auto &it : nodes[i].edges) {
if (it.second != State::S1)
- log(" egde %s -> %d\n", log_signal(it.second), it.first);
+ log(" edge %s -> %d\n", log_signal(it.second), it.first);
else
- log(" egde -> %d\n", it.first);
+ log(" edge -> %d\n", it.first);
}
for (auto &it : nodes[i].links) {
@@ -856,9 +856,9 @@ struct SvaFsm
for (auto &it : unodes[i].edges) {
if (!it.second.empty())
- log(" egde %s -> %d\n", log_signal(it.second), it.first);
+ log(" edge %s -> %d\n", log_signal(it.second), it.first);
else
- log(" egde -> %d\n", it.first);
+ log(" edge -> %d\n", it.first);
}
for (auto &ctrl : unodes[i].accept) {
@@ -1666,7 +1666,20 @@ struct VerificSvaImporter
log(" importing SVA property at root cell %s (%s) at %s:%d.\n", root->Name(), root->View()->Owner()->Name(),
LineFile::GetFileName(root->Linefile()), LineFile::GetLineNo(root->Linefile()));
- RTLIL::IdString root_name = module->uniquify(importer->mode_names || root->IsUserDeclared() ? RTLIL::escape_id(root->Name()) : NEW_ID);
+ bool is_user_declared = root->IsUserDeclared();
+
+ // FIXME
+ if (!is_user_declared) {
+ const char *name = root->Name();
+ for (int i = 0; name[i]; i++) {
+ if (i ? (name[i] < '0' || name[i] > '9') : (name[i] != 'i')) {
+ is_user_declared = true;
+ break;
+ }
+ }
+ }
+
+ RTLIL::IdString root_name = module->uniquify(importer->mode_names || is_user_declared ? RTLIL::escape_id(root->Name()) : NEW_ID);
// parse SVA sequence into trigger signal
diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc
index 8dcc7c5a..504f8b3f 100644
--- a/frontends/verilog/verilog_frontend.cc
+++ b/frontends/verilog/verilog_frontend.cc
@@ -66,12 +66,24 @@ struct VerilogFrontend : public Frontend {
log(" enable support for SystemVerilog assertions and some Yosys extensions\n");
log(" replace the implicit -D SYNTHESIS with -D FORMAL\n");
log("\n");
+ log(" -noassert\n");
+ log(" ignore assert() statements\n");
+ log("\n");
+ log(" -noassume\n");
+ log(" ignore assume() statements\n");
+ log("\n");
log(" -norestrict\n");
- log(" ignore restrict() assertions\n");
+ log(" ignore restrict() statements\n");
log("\n");
log(" -assume-asserts\n");
log(" treat all assert() statements like assume() statements\n");
log("\n");
+ log(" -assert-assumes\n");
+ log(" treat all assume() statements like assert() statements\n");
+ log("\n");
+ log(" -debug\n");
+ log(" alias for -dump_ast1 -dump_ast2 -dump_vlog1 -dump_vlog2 -yydebug\n");
+ log("\n");
log(" -dump_ast1\n");
log(" dump abstract syntax tree (before simplification)\n");
log("\n");
@@ -81,7 +93,10 @@ struct VerilogFrontend : public Frontend {
log(" -no_dump_ptr\n");
log(" do not include hex memory addresses in dump (easier to diff dumps)\n");
log("\n");
- log(" -dump_vlog\n");
+ log(" -dump_vlog1\n");
+ log(" dump ast as Verilog code (before simplification)\n");
+ log("\n");
+ log(" -dump_vlog2\n");
log(" dump ast as Verilog code (after simplification)\n");
log("\n");
log(" -dump_rtlil\n");
@@ -188,7 +203,8 @@ struct VerilogFrontend : public Frontend {
bool flag_dump_ast1 = false;
bool flag_dump_ast2 = false;
bool flag_no_dump_ptr = false;
- bool flag_dump_vlog = false;
+ bool flag_dump_vlog1 = false;
+ bool flag_dump_vlog2 = false;
bool flag_dump_rtlil = false;
bool flag_nolatches = false;
bool flag_nomeminit = false;
@@ -229,6 +245,14 @@ struct VerilogFrontend : public Frontend {
formal_mode = true;
continue;
}
+ if (arg == "-noassert") {
+ noassert_mode = true;
+ continue;
+ }
+ if (arg == "-noassume") {
+ noassume_mode = true;
+ continue;
+ }
if (arg == "-norestrict") {
norestrict_mode = true;
continue;
@@ -237,6 +261,18 @@ struct VerilogFrontend : public Frontend {
assume_asserts_mode = true;
continue;
}
+ if (arg == "-assert-assumes") {
+ assert_assumes_mode = true;
+ continue;
+ }
+ if (arg == "-debug") {
+ flag_dump_ast1 = true;
+ flag_dump_ast2 = true;
+ flag_dump_vlog1 = true;
+ flag_dump_vlog2 = true;
+ frontend_verilog_yydebug = true;
+ continue;
+ }
if (arg == "-dump_ast1") {
flag_dump_ast1 = true;
continue;
@@ -249,8 +285,12 @@ struct VerilogFrontend : public Frontend {
flag_no_dump_ptr = true;
continue;
}
- if (arg == "-dump_vlog") {
- flag_dump_vlog = true;
+ if (arg == "-dump_vlog1") {
+ flag_dump_vlog1 = true;
+ continue;
+ }
+ if (arg == "-dump_vlog2") {
+ flag_dump_vlog2 = true;
continue;
}
if (arg == "-dump_rtlil") {
@@ -389,7 +429,7 @@ 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_vlog, 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, lib_mode, flag_noopt, flag_icells, 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 16edc798..523bbc89 100644
--- a/frontends/verilog/verilog_frontend.h
+++ b/frontends/verilog/verilog_frontend.h
@@ -54,12 +54,21 @@ namespace VERILOG_FRONTEND
// running in -formal mode
extern bool formal_mode;
+ // running in -noassert mode
+ extern bool noassert_mode;
+
+ // running in -noassume mode
+ extern bool noassume_mode;
+
// running in -norestrict mode
extern bool norestrict_mode;
// running in -assume-asserts mode
extern bool assume_asserts_mode;
+ // running in -assert-assumes mode
+ extern bool assert_assumes_mode;
+
// running in -lib mode
extern bool lib_mode;
diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l
index 83921bf0..6ef38252 100644
--- a/frontends/verilog/verilog_lexer.l
+++ b/frontends/verilog/verilog_lexer.l
@@ -135,6 +135,9 @@ YOSYS_NAMESPACE_END
frontend_verilog_yyerror("Unsupported default nettype: %s", p);
}
+"`protect"[^\n]* /* ignore `protect*/
+"`endprotect"[^\n]* /* ignore `endprotect*/
+
"`"[a-zA-Z_$][a-zA-Z0-9_$]* {
frontend_verilog_yyerror("Unimplemented compiler directive or undefined macro %s.", yytext);
}
@@ -150,6 +153,9 @@ YOSYS_NAMESPACE_END
"specparam" { return TOK_SPECPARAM; }
"package" { SV_KEYWORD(TOK_PACKAGE); }
"endpackage" { SV_KEYWORD(TOK_ENDPACKAGE); }
+"interface" { SV_KEYWORD(TOK_INTERFACE); }
+"endinterface" { SV_KEYWORD(TOK_ENDINTERFACE); }
+"modport" { SV_KEYWORD(TOK_MODPORT); }
"parameter" { return TOK_PARAMETER; }
"localparam" { return TOK_LOCALPARAM; }
"defparam" { return TOK_DEFPARAM; }
@@ -183,6 +189,14 @@ YOSYS_NAMESPACE_END
"always_ff" { SV_KEYWORD(TOK_ALWAYS); }
"always_latch" { SV_KEYWORD(TOK_ALWAYS); }
+ /* use special token for labels on assert, assume, cover, and restrict because it's insanley complex
+ 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_$\.] {
+ frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext);
+ return TOK_SVA_LABEL;
+}
+
"assert" { if (formal_mode) return TOK_ASSERT; SV_KEYWORD(TOK_ASSERT); }
"assume" { if (formal_mode) return TOK_ASSUME; SV_KEYWORD(TOK_ASSUME); }
"cover" { if (formal_mode) return TOK_COVER; SV_KEYWORD(TOK_COVER); }
@@ -268,7 +282,7 @@ YOSYS_NAMESPACE_END
yystr[j++] = yystr[i++];
}
yystr[j] = 0;
- frontend_verilog_yylval.string = new std::string(yystr);
+ frontend_verilog_yylval.string = new std::string(yystr, j);
free(yystr);
return TOK_STRING;
}
@@ -295,6 +309,11 @@ supply1 { return TOK_SUPPLY1; }
return TOK_ID;
}
+[a-zA-Z_$][a-zA-Z0-9_$\.]* {
+ frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext);
+ return TOK_ID;
+}
+
"/*"[ \t]*(synopsys|synthesis)[ \t]*translate_off[ \t]*"*/" {
static bool printed_warning = false;
if (!printed_warning) {
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
index 2389d7d3..52685f63 100644
--- a/frontends/verilog/verilog_parser.y
+++ b/frontends/verilog/verilog_parser.y
@@ -35,6 +35,7 @@
%{
#include <list>
+#include <stack>
#include <string.h>
#include "frontends/verilog/verilog_frontend.h"
#include "kernel/log.h"
@@ -47,7 +48,8 @@ YOSYS_NAMESPACE_BEGIN
namespace VERILOG_FRONTEND {
int port_counter;
std::map<std::string, int> port_stubs;
- std::map<std::string, AstNode*> attr_list, default_attr_list;
+ std::map<std::string, AstNode*> *attr_list, default_attr_list;
+ std::stack<std::map<std::string, AstNode*> *> attr_list_stack;
std::map<std::string, AstNode*> *albuf;
std::vector<AstNode*> ast_stack;
struct AstNode *astbuf1, *astbuf2, *astbuf3;
@@ -58,8 +60,10 @@ namespace VERILOG_FRONTEND {
bool do_not_require_port_stubs;
bool default_nettype_wire;
bool sv_mode, formal_mode, lib_mode;
- bool norestrict_mode, assume_asserts_mode;
+ bool noassert_mode, noassume_mode, norestrict_mode;
+ bool assume_asserts_mode, assert_assumes_mode;
bool current_wire_rand, current_wire_const;
+ bool current_modport_input, current_modport_output;
std::istream *lexin;
}
YOSYS_NAMESPACE_END
@@ -101,10 +105,12 @@ static void free_attr(std::map<std::string, AstNode*> *al)
bool boolean;
}
-%token <string> TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE
+%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 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_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL
%token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT
@@ -114,15 +120,14 @@ static void free_attr(std::map<std::string, AstNode*> *al)
%token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR TOK_REAL
%token TOK_SYNOPSYS_FULL_CASE TOK_SYNOPSYS_PARALLEL_CASE
%token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED
-%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_ASSERT TOK_ASSUME
-%token TOK_RESTRICT TOK_COVER TOK_PROPERTY TOK_ENUM TOK_TYPEDEF
+%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_PROPERTY TOK_ENUM TOK_TYPEDEF
%token TOK_RAND TOK_CONST TOK_CHECKER TOK_ENDCHECKER TOK_EVENTUALLY
%token TOK_INCREMENT TOK_DECREMENT TOK_UNIQUE TOK_PRIORITY
%type <ast> range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int
%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list
-%type <string> opt_label tok_prim_wrapper hierarchical_id
-%type <boolean> opt_signed unique_case_attr
+%type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id
+%type <boolean> opt_signed opt_property unique_case_attr
%type <al> attr case_attr
// operator precedence from low to high
@@ -167,19 +172,23 @@ design:
param_decl design |
localparam_decl design |
package design |
+ interface design |
/* empty */;
attr:
{
- for (auto &it : attr_list)
- delete it.second;
- attr_list.clear();
+ if (attr_list != nullptr)
+ attr_list_stack.push(attr_list);
+ attr_list = new std::map<std::string, AstNode*>;
for (auto &it : default_attr_list)
- attr_list[it.first] = it.second->clone();
+ (*attr_list)[it.first] = it.second->clone();
} attr_opt {
- std::map<std::string, AstNode*> *al = new std::map<std::string, AstNode*>;
- al->swap(attr_list);
- $$ = al;
+ $$ = attr_list;
+ if (!attr_list_stack.empty()) {
+ attr_list = attr_list_stack.top();
+ attr_list_stack.pop();
+ } else
+ attr_list = nullptr;
};
attr_opt:
@@ -188,15 +197,20 @@ attr_opt:
defattr:
DEFATTR_BEGIN {
+ if (attr_list != nullptr)
+ attr_list_stack.push(attr_list);
+ attr_list = new std::map<std::string, AstNode*>;
for (auto &it : default_attr_list)
delete it.second;
default_attr_list.clear();
- for (auto &it : attr_list)
- delete it.second;
- attr_list.clear();
} opt_attr_list {
- default_attr_list = attr_list;
- attr_list.clear();
+ attr_list->swap(default_attr_list);
+ delete attr_list;
+ if (!attr_list_stack.empty()) {
+ attr_list = attr_list_stack.top();
+ attr_list_stack.pop();
+ } else
+ attr_list = nullptr;
} DEFATTR_END;
opt_attr_list:
@@ -208,15 +222,15 @@ attr_list:
attr_assign:
hierarchical_id {
- if (attr_list.count(*$1) != 0)
- delete attr_list[*$1];
- attr_list[*$1] = AstNode::mkconst_int(1, false);
+ if (attr_list->count(*$1) != 0)
+ delete (*attr_list)[*$1];
+ (*attr_list)[*$1] = AstNode::mkconst_int(1, false);
delete $1;
} |
hierarchical_id '=' expr {
- if (attr_list.count(*$1) != 0)
- delete attr_list[*$1];
- attr_list[*$1] = $3;
+ if (attr_list->count(*$1) != 0)
+ delete (*attr_list)[*$1];
+ (*attr_list)[*$1] = $3;
delete $1;
};
@@ -301,7 +315,7 @@ module_arg_opt_assignment:
else
ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2));
} else
- frontend_verilog_yyerror("Syntax error.");
+ frontend_verilog_yyerror("SystemVerilog interface in module port list cannot have a default value.");
} |
/* empty */;
@@ -319,6 +333,21 @@ module_arg:
}
delete $1;
} module_arg_opt_assignment |
+ TOK_ID {
+ astbuf1 = new AstNode(AST_INTERFACEPORT);
+ astbuf1->children.push_back(new AstNode(AST_INTERFACEPORTTYPE));
+ astbuf1->children[0]->str = *$1;
+ delete $1;
+ } TOK_ID { /* SV interfaces */
+ if (!sv_mode)
+ frontend_verilog_yyerror("Interface found in port list (%s). This is not supported unless read_verilog is called with -sv!", $3->c_str());
+ astbuf2 = astbuf1->clone(); // really only needed if multiple instances of same type.
+ astbuf2->str = *$3;
+ delete $3;
+ astbuf2->port_id = ++port_counter;
+ ast_stack.back()->children.push_back(astbuf2);
+ delete astbuf1; // really only needed if multiple instances of same type.
+ } module_arg_opt_assignment |
attr wire_type range TOK_ID {
AstNode *node = $2;
node->str = *$4;
@@ -356,6 +385,33 @@ package_body:
package_body_stmt:
localparam_decl;
+interface:
+ TOK_INTERFACE TOK_ID {
+ do_not_require_port_stubs = false;
+ AstNode *intf = new AstNode(AST_INTERFACE);
+ ast_stack.back()->children.push_back(intf);
+ ast_stack.push_back(intf);
+ current_ast_mod = intf;
+ port_stubs.clear();
+ port_counter = 0;
+ intf->str = *$2;
+ delete $2;
+ } module_para_opt module_args_opt ';' interface_body TOK_ENDINTERFACE {
+ if (port_stubs.size() != 0)
+ frontend_verilog_yyerror("Missing details for module port `%s'.",
+ port_stubs.begin()->first.c_str());
+ ast_stack.pop_back();
+ log_assert(ast_stack.size() == 1);
+ current_ast_mod = NULL;
+ };
+
+interface_body:
+ interface_body interface_body_stmt |;
+
+interface_body_stmt:
+ param_decl | localparam_decl | defparam_decl | wire_decl | always_stmt | assign_stmt |
+ modport_stmt;
+
non_opt_delay:
'#' TOK_ID { delete $2; } |
'#' TOK_CONSTVAL { delete $2; } |
@@ -626,7 +682,7 @@ task_func_port:
astbuf2 = $3;
if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) {
if (astbuf2) {
- frontend_verilog_yyerror("Syntax error.");
+ frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions (task/function arguments)");
} else {
astbuf2 = new AstNode(AST_RANGE);
astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true));
@@ -634,7 +690,7 @@ task_func_port:
}
}
if (astbuf2 && astbuf2->children.size() != 2)
- frontend_verilog_yyerror("Syntax error.");
+ frontend_verilog_yyerror("task/function argument range must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]");
} wire_name | wire_name;
task_func_body:
@@ -738,7 +794,7 @@ more_path_inputs :
list_of_path_outputs :
specify_output_terminal_descriptor |
list_of_path_outputs ',' specify_output_terminal_descriptor ;
-
+
opt_polarity_operator :
'+'
| '-'
@@ -763,7 +819,7 @@ system_timing_arg :
system_timing_args :
system_timing_arg |
system_timing_args ',' system_timing_arg ;
-
+
/*
t_path_delay_expression :
path_delay_expression;
@@ -825,7 +881,7 @@ constant_mintypmax_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 assing runtime checks for constant-ness
+// (such as param assignment), so we may leave this as-is, perhaps adding runtime checks for constant-ness
constant_expression:
expr ;
@@ -837,7 +893,7 @@ param_signed:
param_integer:
TOK_INTEGER {
if (astbuf1->children.size() != 1)
- frontend_verilog_yyerror("Syntax error.");
+ frontend_verilog_yyerror("Internal error in param_integer - should not happen?");
astbuf1->children.push_back(new AstNode(AST_RANGE));
astbuf1->children.back()->children.push_back(AstNode::mkconst_int(31, true));
astbuf1->children.back()->children.push_back(AstNode::mkconst_int(0, true));
@@ -847,7 +903,7 @@ param_integer:
param_real:
TOK_REAL {
if (astbuf1->children.size() != 1)
- frontend_verilog_yyerror("Syntax error.");
+ frontend_verilog_yyerror("Parameter already declared as integer, cannot set to real.");
astbuf1->children.push_back(new AstNode(AST_REALVALUE));
} | /* empty */;
@@ -855,7 +911,7 @@ param_range:
range {
if ($1 != NULL) {
if (astbuf1->children.size() != 1)
- frontend_verilog_yyerror("Syntax error.");
+ frontend_verilog_yyerror("integer/real parameters should not have a range.");
astbuf1->children.push_back($1);
}
};
@@ -881,9 +937,15 @@ param_decl_list:
single_param_decl:
TOK_ID '=' expr {
- if (astbuf1 == nullptr)
- frontend_verilog_yyerror("syntax error");
- AstNode *node = astbuf1->clone();
+ AstNode *node;
+ if (astbuf1 == nullptr) {
+ if (!sv_mode)
+ frontend_verilog_yyerror("In pure Verilog (not SystemVerilog), parameter/localparam with an initializer must use the parameter/localparam keyword");
+ node = new AstNode(AST_PARAMETER);
+ node->children.push_back(AstNode::mkconst_int(0, true));
+ } else {
+ node = astbuf1->clone();
+ }
node->str = *$1;
delete node->children[0];
node->children[0] = $3;
@@ -914,7 +976,7 @@ wire_decl:
astbuf2 = $3;
if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) {
if (astbuf2) {
- frontend_verilog_yyerror("Syntax error.");
+ frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions.");
} else {
astbuf2 = new AstNode(AST_RANGE);
astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true));
@@ -922,7 +984,7 @@ wire_decl:
}
}
if (astbuf2 && astbuf2->children.size() != 2)
- frontend_verilog_yyerror("Syntax error.");
+ frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]");
} wire_name_list {
delete astbuf1;
if (astbuf2 != NULL)
@@ -1016,7 +1078,7 @@ wire_name_and_opt_assign:
wire_name:
TOK_ID range_or_multirange {
if (astbuf1 == nullptr)
- frontend_verilog_yyerror("Syntax error.");
+ frontend_verilog_yyerror("Internal error - should not happen - no AST_WIRE node.");
AstNode *node = astbuf1->clone();
node->str = *$1;
append_attr_clone(node, albuf);
@@ -1024,7 +1086,7 @@ wire_name:
node->children.push_back(astbuf2->clone());
if ($2 != NULL) {
if (node->is_input || node->is_output)
- frontend_verilog_yyerror("Syntax error.");
+ frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions.");
if (!astbuf2) {
AstNode *rng = new AstNode(AST_RANGE);
rng->children.push_back(AstNode::mkconst_int(0, true));
@@ -1267,74 +1329,216 @@ opt_label:
$$ = NULL;
};
+opt_sva_label:
+ TOK_SVA_LABEL ':' {
+ $$ = $1;
+ } |
+ /* empty */ {
+ $$ = NULL;
+ };
+
opt_property:
- TOK_PROPERTY | /* empty */;
+ TOK_PROPERTY {
+ $$ = true;
+ } |
+ /* empty */ {
+ $$ = false;
+ };
-opt_stmt_label:
- TOK_ID ':' | /* empty */;
+modport_stmt:
+ TOK_MODPORT TOK_ID {
+ AstNode *modport = new AstNode(AST_MODPORT);
+ ast_stack.back()->children.push_back(modport);
+ ast_stack.push_back(modport);
+ modport->str = *$2;
+ delete $2;
+ } modport_args_opt {
+ ast_stack.pop_back();
+ log_assert(ast_stack.size() == 2);
+ } ';'
+
+modport_args_opt:
+ '(' ')' | '(' modport_args optional_comma ')';
+
+modport_args:
+ modport_arg | modport_args ',' modport_arg;
+
+modport_arg:
+ modport_type_token modport_member |
+ modport_member
+
+modport_member:
+ TOK_ID {
+ AstNode *modport_member = new AstNode(AST_MODPORTMEMBER);
+ ast_stack.back()->children.push_back(modport_member);
+ modport_member->str = *$1;
+ modport_member->is_input = current_modport_input;
+ modport_member->is_output = current_modport_output;
+ delete $1;
+ }
+
+modport_type_token:
+ TOK_INPUT {current_modport_input = 1; current_modport_output = 0;} | TOK_OUTPUT {current_modport_input = 0; current_modport_output = 1;}
assert:
- opt_stmt_label TOK_ASSERT opt_property '(' expr ')' ';' {
- ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5));
+ opt_sva_label TOK_ASSERT opt_property '(' expr ')' ';' {
+ if (noassert_mode) {
+ delete $5;
+ } else {
+ AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5);
+ if ($1 != nullptr)
+ node->str = *$1;
+ ast_stack.back()->children.push_back(node);
+ }
+ if ($1 != nullptr)
+ delete $1;
} |
- opt_stmt_label TOK_ASSUME opt_property '(' expr ')' ';' {
- ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5));
+ opt_sva_label TOK_ASSUME opt_property '(' expr ')' ';' {
+ if (noassume_mode) {
+ delete $5;
+ } else {
+ AstNode *node = new AstNode(assert_assumes_mode ? AST_ASSERT : AST_ASSUME, $5);
+ if ($1 != nullptr)
+ node->str = *$1;
+ ast_stack.back()->children.push_back(node);
+ }
+ if ($1 != nullptr)
+ delete $1;
} |
- opt_stmt_label TOK_ASSERT opt_property '(' TOK_EVENTUALLY expr ')' ';' {
- ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6));
+ opt_sva_label TOK_ASSERT opt_property '(' TOK_EVENTUALLY expr ')' ';' {
+ if (noassert_mode) {
+ delete $6;
+ } else {
+ AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6);
+ if ($1 != nullptr)
+ node->str = *$1;
+ ast_stack.back()->children.push_back(node);
+ }
+ if ($1 != nullptr)
+ delete $1;
} |
- opt_stmt_label TOK_ASSUME opt_property '(' TOK_EVENTUALLY expr ')' ';' {
- ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6));
+ opt_sva_label TOK_ASSUME opt_property '(' TOK_EVENTUALLY expr ')' ';' {
+ if (noassume_mode) {
+ delete $6;
+ } else {
+ AstNode *node = new AstNode(assert_assumes_mode ? AST_LIVE : AST_FAIR, $6);
+ if ($1 != nullptr)
+ node->str = *$1;
+ ast_stack.back()->children.push_back(node);
+ }
+ if ($1 != nullptr)
+ delete $1;
} |
- opt_stmt_label TOK_COVER opt_property '(' expr ')' ';' {
- ast_stack.back()->children.push_back(new AstNode(AST_COVER, $5));
+ opt_sva_label TOK_COVER opt_property '(' expr ')' ';' {
+ AstNode *node = new AstNode(AST_COVER, $5);
+ if ($1 != nullptr) {
+ node->str = *$1;
+ delete $1;
+ }
+ ast_stack.back()->children.push_back(node);
} |
- opt_stmt_label TOK_COVER opt_property '(' ')' ';' {
- ast_stack.back()->children.push_back(new AstNode(AST_COVER, AstNode::mkconst_int(1, false)));
+ opt_sva_label TOK_COVER opt_property '(' ')' ';' {
+ AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false));
+ if ($1 != nullptr) {
+ node->str = *$1;
+ delete $1;
+ }
+ ast_stack.back()->children.push_back(node);
} |
- opt_stmt_label TOK_COVER ';' {
- ast_stack.back()->children.push_back(new AstNode(AST_COVER, AstNode::mkconst_int(1, false)));
+ opt_sva_label TOK_COVER ';' {
+ AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false));
+ if ($1 != nullptr) {
+ node->str = *$1;
+ delete $1;
+ }
+ ast_stack.back()->children.push_back(node);
} |
- opt_stmt_label TOK_RESTRICT opt_property '(' expr ')' ';' {
- if (norestrict_mode)
+ opt_sva_label TOK_RESTRICT opt_property '(' expr ')' ';' {
+ if (norestrict_mode) {
delete $5;
- else
- ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5));
+ } else {
+ AstNode *node = new AstNode(AST_ASSUME, $5);
+ if ($1 != nullptr)
+ node->str = *$1;
+ ast_stack.back()->children.push_back(node);
+ }
+ if (!$3)
+ log_file_warning(current_filename, get_line_num(), "SystemVerilog does not allow \"restrict\" without \"property\".\n");
+ if ($1 != nullptr)
+ delete $1;
} |
- opt_stmt_label TOK_RESTRICT opt_property '(' TOK_EVENTUALLY expr ')' ';' {
- if (norestrict_mode)
+ opt_sva_label TOK_RESTRICT opt_property '(' TOK_EVENTUALLY expr ')' ';' {
+ if (norestrict_mode) {
delete $6;
- else
- ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6));
+ } else {
+ AstNode *node = new AstNode(AST_FAIR, $6);
+ if ($1 != nullptr)
+ node->str = *$1;
+ ast_stack.back()->children.push_back(node);
+ }
+ if (!$3)
+ log_file_warning(current_filename, get_line_num(), "SystemVerilog does not allow \"restrict\" without \"property\".\n");
+ if ($1 != nullptr)
+ delete $1;
};
assert_property:
- TOK_ASSERT TOK_PROPERTY '(' expr ')' ';' {
- ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $4));
- } |
- TOK_ASSUME TOK_PROPERTY '(' expr ')' ';' {
- ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $4));
+ opt_sva_label TOK_ASSERT TOK_PROPERTY '(' expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5));
+ if ($1 != nullptr) {
+ ast_stack.back()->children.back()->str = *$1;
+ delete $1;
+ }
} |
- TOK_ASSERT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' {
- ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $5));
+ opt_sva_label TOK_ASSUME TOK_PROPERTY '(' expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5));
+ if ($1 != nullptr) {
+ ast_stack.back()->children.back()->str = *$1;
+ delete $1;
+ }
} |
- TOK_ASSUME TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' {
- ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $5));
+ opt_sva_label TOK_ASSERT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6));
+ if ($1 != nullptr) {
+ ast_stack.back()->children.back()->str = *$1;
+ delete $1;
+ }
} |
- TOK_COVER TOK_PROPERTY '(' expr ')' ';' {
- ast_stack.back()->children.push_back(new AstNode(AST_COVER, $4));
+ opt_sva_label TOK_ASSUME TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6));
+ if ($1 != nullptr) {
+ ast_stack.back()->children.back()->str = *$1;
+ delete $1;
+ }
} |
- TOK_RESTRICT TOK_PROPERTY '(' expr ')' ';' {
- if (norestrict_mode)
- delete $4;
- else
- ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $4));
+ opt_sva_label TOK_COVER TOK_PROPERTY '(' expr ')' ';' {
+ ast_stack.back()->children.push_back(new AstNode(AST_COVER, $5));
+ if ($1 != nullptr) {
+ ast_stack.back()->children.back()->str = *$1;
+ delete $1;
+ }
} |
- TOK_RESTRICT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' {
- if (norestrict_mode)
+ opt_sva_label TOK_RESTRICT TOK_PROPERTY '(' expr ')' ';' {
+ if (norestrict_mode) {
delete $5;
- else
- ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $5));
+ } else {
+ ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5));
+ if ($1 != nullptr) {
+ ast_stack.back()->children.back()->str = *$1;
+ delete $1;
+ }
+ }
+ } |
+ opt_sva_label TOK_RESTRICT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' {
+ if (norestrict_mode) {
+ delete $6;
+ } else {
+ ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6));
+ if ($1 != nullptr) {
+ ast_stack.back()->children.back()->str = *$1;
+ delete $1;
+ }
+ }
};
simple_behavioral_stmt:
@@ -1379,7 +1583,7 @@ behavioral_stmt:
node->str = *$3;
} behavioral_stmt_list TOK_END opt_label {
if ($3 != NULL && $7 != NULL && *$3 != *$7)
- frontend_verilog_yyerror("Syntax error.");
+ frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $3->c_str()+1, $7->c_str()+1);
if ($3 != NULL)
delete $3;
if ($7 != NULL)
@@ -1552,6 +1756,11 @@ case_expr_list:
TOK_DEFAULT {
ast_stack.back()->children.push_back(new AstNode(AST_DEFAULT));
} |
+ TOK_SVA_LABEL {
+ ast_stack.back()->children.push_back(new AstNode(AST_IDENTIFIER));
+ ast_stack.back()->children.back()->str = *$1;
+ delete $1;
+ } |
expr {
ast_stack.back()->children.push_back($1);
} |
@@ -1695,7 +1904,7 @@ basic_expr:
} |
'(' expr ')' TOK_CONSTVAL {
if ($4->substr(0, 1) != "'")
- frontend_verilog_yyerror("Syntax error.");
+ frontend_verilog_yyerror("Cast operation must be applied on sized constants e.g. (<expr>)<constval> , while %s is not a sized constant.", $4->c_str());
AstNode *bits = $2;
AstNode *val = const2ast(*$4, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode);
if (val == NULL)
@@ -1705,7 +1914,7 @@ basic_expr:
} |
hierarchical_id TOK_CONSTVAL {
if ($2->substr(0, 1) != "'")
- frontend_verilog_yyerror("Syntax error.");
+ frontend_verilog_yyerror("Cast operation must be applied on sized constants, e.g. <ID>\'d0, while %s is not a sized constant.", $2->c_str());
AstNode *bits = new AstNode(AST_IDENTIFIER);
bits->str = *$1;
AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode);
diff --git a/kernel/celltypes.h b/kernel/celltypes.h
index fcc4fcc4..ae88f4aa 100644
--- a/kernel/celltypes.h
+++ b/kernel/celltypes.h
@@ -82,6 +82,27 @@ struct CellTypes
void setup_internals()
{
+ setup_internals_eval();
+
+ IdString A = "\\A", B = "\\B", EN = "\\EN", Y = "\\Y";
+
+ setup_type("$tribuf", {A, EN}, {Y}, true);
+
+ setup_type("$assert", {A, EN}, pool<RTLIL::IdString>(), true);
+ setup_type("$assume", {A, EN}, pool<RTLIL::IdString>(), true);
+ setup_type("$live", {A, EN}, pool<RTLIL::IdString>(), true);
+ setup_type("$fair", {A, EN}, pool<RTLIL::IdString>(), true);
+ setup_type("$cover", {A, EN}, pool<RTLIL::IdString>(), true);
+ setup_type("$initstate", pool<RTLIL::IdString>(), {Y}, true);
+ setup_type("$anyconst", pool<RTLIL::IdString>(), {Y}, true);
+ setup_type("$anyseq", pool<RTLIL::IdString>(), {Y}, true);
+ setup_type("$allconst", pool<RTLIL::IdString>(), {Y}, true);
+ setup_type("$allseq", pool<RTLIL::IdString>(), {Y}, true);
+ setup_type("$equiv", {A, B}, {Y}, true);
+ }
+
+ void setup_internals_eval()
+ {
std::vector<RTLIL::IdString> unary_ops = {
"$not", "$pos", "$neg",
"$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_xnor", "$reduce_bool",
@@ -111,20 +132,6 @@ struct CellTypes
setup_type("$lcu", {P, G, CI}, {CO}, true);
setup_type("$alu", {A, B, CI, BI}, {X, Y, CO}, true);
setup_type("$fa", {A, B, C}, {X, Y}, true);
-
- setup_type("$tribuf", {A, EN}, {Y}, true);
-
- setup_type("$assert", {A, EN}, pool<RTLIL::IdString>(), true);
- setup_type("$assume", {A, EN}, pool<RTLIL::IdString>(), true);
- setup_type("$live", {A, EN}, pool<RTLIL::IdString>(), true);
- setup_type("$fair", {A, EN}, pool<RTLIL::IdString>(), true);
- setup_type("$cover", {A, EN}, pool<RTLIL::IdString>(), true);
- setup_type("$initstate", pool<RTLIL::IdString>(), {Y}, true);
- setup_type("$anyconst", pool<RTLIL::IdString>(), {Y}, true);
- setup_type("$anyseq", pool<RTLIL::IdString>(), {Y}, true);
- setup_type("$allconst", pool<RTLIL::IdString>(), {Y}, true);
- setup_type("$allseq", pool<RTLIL::IdString>(), {Y}, true);
- setup_type("$equiv", {A, B}, {Y}, true);
}
void setup_internals_mem()
@@ -154,10 +161,19 @@ struct CellTypes
void setup_stdcells()
{
+ setup_stdcells_eval();
+
+ IdString A = "\\A", E = "\\E", Y = "\\Y";
+
+ setup_type("$_TBUF_", {A, E}, {Y}, true);
+ }
+
+ void setup_stdcells_eval()
+ {
IdString A = "\\A", B = "\\B", C = "\\C", D = "\\D";
IdString E = "\\E", F = "\\F", G = "\\G", H = "\\H";
IdString I = "\\I", J = "\\J", K = "\\K", L = "\\L";
- IdString M = "\\I", N = "\\N", O = "\\O", P = "\\P";
+ IdString M = "\\M", N = "\\N", O = "\\O", P = "\\P";
IdString S = "\\S", T = "\\T", U = "\\U", V = "\\V";
IdString Y = "\\Y";
@@ -179,7 +195,6 @@ struct CellTypes
setup_type("$_OAI3_", {A, B, C}, {Y}, true);
setup_type("$_AOI4_", {A, B, C, D}, {Y}, true);
setup_type("$_OAI4_", {A, B, C, D}, {Y}, true);
- setup_type("$_TBUF_", {A, E}, {Y}, true);
}
void setup_stdcells_mem()
@@ -257,7 +272,7 @@ struct CellTypes
return v;
}
- static RTLIL::Const eval(RTLIL::IdString type, const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+ static RTLIL::Const eval(RTLIL::IdString type, const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len, bool *errp = nullptr)
{
if (type == "$sshr" && !signed1)
type = "$shr";
@@ -329,10 +344,15 @@ struct CellTypes
if (type == "$_ORNOT_")
return const_or(arg1, eval_not(arg2), false, false, 1);
+ if (errp != nullptr) {
+ *errp = true;
+ return State::Sm;
+ }
+
log_abort();
}
- static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2)
+ static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool *errp = nullptr)
{
if (cell->type == "$slice") {
RTLIL::Const ret;
@@ -415,10 +435,10 @@ struct CellTypes
bool signed_a = cell->parameters.count("\\A_SIGNED") > 0 && cell->parameters["\\A_SIGNED"].as_bool();
bool signed_b = cell->parameters.count("\\B_SIGNED") > 0 && cell->parameters["\\B_SIGNED"].as_bool();
int result_len = cell->parameters.count("\\Y_WIDTH") > 0 ? cell->parameters["\\Y_WIDTH"].as_int() : -1;
- return eval(cell->type, arg1, arg2, signed_a, signed_b, result_len);
+ return eval(cell->type, arg1, arg2, signed_a, signed_b, result_len, errp);
}
- static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3)
+ static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3, bool *errp = nullptr)
{
if (cell->type.in("$mux", "$pmux", "$_MUX_")) {
RTLIL::Const ret = arg1;
@@ -436,10 +456,10 @@ struct CellTypes
return eval_not(const_and(const_or(arg1, arg2, false, false, 1), arg3, false, false, 1));
log_assert(arg3.bits.size() == 0);
- return eval(cell, arg1, arg2);
+ return eval(cell, arg1, arg2, errp);
}
- static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3, const RTLIL::Const &arg4)
+ static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3, const RTLIL::Const &arg4, bool *errp = nullptr)
{
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));
@@ -447,7 +467,7 @@ struct CellTypes
return eval_not(const_and(const_or(arg1, arg2, false, false, 1), const_and(arg3, arg4, false, false, 1), false, false, 1));
log_assert(arg4.bits.size() == 0);
- return eval(cell, arg1, arg2, arg3);
+ return eval(cell, arg1, arg2, arg3, errp);
}
};
diff --git a/kernel/consteval.h b/kernel/consteval.h
index 0229f504..154373a8 100644
--- a/kernel/consteval.h
+++ b/kernel/consteval.h
@@ -321,8 +321,13 @@ struct ConstEval
if (sig_d.size() > 0 && !eval(sig_d, undef, cell))
return false;
- set(sig_y, CellTypes::eval(cell, sig_a.as_const(), sig_b.as_const(),
- sig_c.as_const(), sig_d.as_const()));
+ bool eval_err = false;
+ RTLIL::Const eval_ret = CellTypes::eval(cell, sig_a.as_const(), sig_b.as_const(), sig_c.as_const(), sig_d.as_const(), &eval_err);
+
+ if (eval_err)
+ return false;
+
+ set(sig_y, eval_ret);
}
return true;
diff --git a/kernel/driver.cc b/kernel/driver.cc
index 17864110..a0bb7e60 100644
--- a/kernel/driver.cc
+++ b/kernel/driver.cc
@@ -179,6 +179,7 @@ int main(int argc, char **argv)
{
std::string frontend_command = "auto";
std::string backend_command = "auto";
+ std::vector<std::string> vlog_defines;
std::vector<std::string> passes_commands;
std::vector<std::string> plugin_filenames;
std::string output_filename = "";
@@ -268,7 +269,10 @@ int main(int argc, char **argv)
printf(" -A\n");
printf(" will call abort() at the end of the script. for debugging\n");
printf("\n");
- printf(" -D <header_id>[:<filename>]\n");
+ printf(" -D <macro>[=<value>]\n");
+ printf(" set the specified Verilog define (via \"read -define\")\n");
+ printf("\n");
+ printf(" -P <header_id>[:<filename>]\n");
printf(" dump the design when printing the specified log header to a file.\n");
printf(" yosys_dump_<header_id>.il is used as filename if none is specified.\n");
printf(" Use 'ALL' as <header_id> to dump at every header.\n");
@@ -307,7 +311,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:E:")) != -1)
+ while ((opt = getopt(argc, argv, "MXAQTVSm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:")) != -1)
{
switch (opt)
{
@@ -408,6 +412,9 @@ int main(int argc, char **argv)
std::regex_constants::egrep));
break;
case 'D':
+ vlog_defines.push_back(optarg);
+ break;
+ case 'P':
{
auto args = split_tokens(optarg, ":");
if (!args.empty() && args[0] == "ALL") {
@@ -473,6 +480,13 @@ int main(int argc, char **argv)
shell(yosys_design);
}
+ if (!vlog_defines.empty()) {
+ std::string vdef_cmd = "read -define";
+ for (auto vdef : vlog_defines)
+ vdef_cmd += " " + vdef;
+ run_pass(vdef_cmd);
+ }
+
while (optind < argc)
run_frontend(argv[optind++], frontend_command, output_filename == "-" ? &backend_command : NULL);
diff --git a/kernel/hashlib.h b/kernel/hashlib.h
index df534ec1..e7cb312e 100644
--- a/kernel/hashlib.h
+++ b/kernel/hashlib.h
@@ -557,9 +557,11 @@ public:
void clear() { hashtable.clear(); entries.clear(); }
iterator begin() { return iterator(this, int(entries.size())-1); }
+ iterator element(int n) { return iterator(this, int(entries.size())-1-n); }
iterator end() { return iterator(nullptr, -1); }
const_iterator begin() const { return const_iterator(this, int(entries.size())-1); }
+ const_iterator element(int n) const { return const_iterator(this, int(entries.size())-1-n); }
const_iterator end() const { return const_iterator(nullptr, -1); }
};
@@ -881,9 +883,11 @@ public:
void clear() { hashtable.clear(); entries.clear(); }
iterator begin() { return iterator(this, int(entries.size())-1); }
+ iterator element(int n) { return iterator(this, int(entries.size())-1-n); }
iterator end() { return iterator(nullptr, -1); }
const_iterator begin() const { return const_iterator(this, int(entries.size())-1); }
+ const_iterator element(int n) const { return const_iterator(this, int(entries.size())-1-n); }
const_iterator end() const { return const_iterator(nullptr, -1); }
};
@@ -952,6 +956,7 @@ public:
void clear() { database.clear(); }
const_iterator begin() const { return database.begin(); }
+ const_iterator element(int n) const { return database.element(n); }
const_iterator end() const { return database.end(); }
};
@@ -1051,6 +1056,7 @@ public:
void clear() { database.clear(); parents.clear(); }
const_iterator begin() const { return database.begin(); }
+ const_iterator element(int n) const { return database.element(n); }
const_iterator end() const { return database.end(); }
};
diff --git a/kernel/log.cc b/kernel/log.cc
index 0ee2170a..400a549d 100644
--- a/kernel/log.cc
+++ b/kernel/log.cc
@@ -196,7 +196,11 @@ void logv_header(RTLIL::Design *design, const char *format, va_list ap)
if (log_hdump.count(header_id) && design != nullptr)
for (auto &filename : log_hdump.at(header_id)) {
log("Dumping current design to '%s'.\n", filename.c_str());
+ if (yosys_xtrace)
+ IdString::xtrace_db_dump();
Pass::call(design, {"dump", "-o", filename});
+ if (yosys_xtrace)
+ log("#X# -- end of dump --\n");
}
if (pop_errfile)
diff --git a/kernel/log.h b/kernel/log.h
index 0b4905c3..75993902 100644
--- a/kernel/log.h
+++ b/kernel/log.h
@@ -94,7 +94,9 @@ const char *log_signal(const RTLIL::SigSpec &sig, bool autoint = true);
const char *log_const(const RTLIL::Const &value, bool autoint = true);
const char *log_id(RTLIL::IdString id);
-template<typename T> static inline const char *log_id(T *obj) {
+template<typename T> static inline const char *log_id(T *obj, const char *nullstr = nullptr) {
+ if (nullstr && obj == nullptr)
+ return nullstr;
return log_id(obj->name);
}
@@ -195,7 +197,7 @@ struct PerformanceTimer
t += 1000000000ULL * (int64_t) rusage.ru_stime.tv_sec + (int64_t) rusage.ru_stime.tv_usec * 1000ULL;
return t;
# else
-# error Dont know how to measure per-process CPU time. Need alternative method (times()/clocks()/gettimeofday()?).
+# error "Don't know how to measure per-process CPU time. Need alternative method (times()/clocks()/gettimeofday()?)."
# endif
}
diff --git a/kernel/register.cc b/kernel/register.cc
index 402a5b3e..64956401 100644
--- a/kernel/register.cc
+++ b/kernel/register.cc
@@ -86,6 +86,8 @@ Pass::pre_post_exec_state_t Pass::pre_execute()
void Pass::post_execute(Pass::pre_post_exec_state_t state)
{
+ IdString::checkpoint();
+
int64_t time_ns = PerformanceTimer::query() - state.begin_ns;
runtime_ns += time_ns;
current_pass = state.parent_pass;
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
index a4fa2cf0..b3214579 100644
--- a/kernel/rtlil.cc
+++ b/kernel/rtlil.cc
@@ -33,6 +33,8 @@ std::vector<int> RTLIL::IdString::global_refcount_storage_;
std::vector<char*> RTLIL::IdString::global_id_storage_;
dict<char*, int, hash_cstr_ops> RTLIL::IdString::global_id_index_;
std::vector<int> RTLIL::IdString::global_free_idx_list_;
+int RTLIL::IdString::last_created_idx_[8];
+int RTLIL::IdString::last_created_idx_ptr_;
RTLIL::Const::Const()
{
@@ -639,6 +641,11 @@ RTLIL::Module::~Module()
delete it->second;
}
+void RTLIL::Module::reprocess_module(RTLIL::Design *, dict<RTLIL::IdString, RTLIL::Module *>)
+{
+ log_error("Cannot reprocess_module module `%s' !\n", id2cstr(name));
+}
+
RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, dict<RTLIL::IdString, RTLIL::Const>, bool mayfail)
{
if (mayfail)
@@ -646,6 +653,14 @@ RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, dict<RTLIL::IdString, RTLI
log_error("Module `%s' is used with parameters but is not parametric!\n", id2cstr(name));
}
+
+RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, dict<RTLIL::IdString, RTLIL::Const>, dict<RTLIL::IdString, RTLIL::Module*>, dict<RTLIL::IdString, RTLIL::IdString>, bool mayfail)
+{
+ if (mayfail)
+ return RTLIL::IdString();
+ log_error("Module `%s' is used with parameters but is not parametric!\n", id2cstr(name));
+}
+
size_t RTLIL::Module::count_id(RTLIL::IdString id)
{
return wires_.count(id) + memories.count(id) + cells_.count(id) + processes.count(id);
@@ -745,7 +760,7 @@ namespace {
void check()
{
- if (cell->type.substr(0, 1) != "$" || cell->type.substr(0, 3) == "$__" || cell->type.substr(0, 8) == "$paramod" ||
+ if (cell->type.substr(0, 1) != "$" || cell->type.substr(0, 3) == "$__" || cell->type.substr(0, 8) == "$paramod" || cell->type.substr(0,10) == "$fmcombine" ||
cell->type.substr(0, 9) == "$verific$" || cell->type.substr(0, 7) == "$array:" || cell->type.substr(0, 8) == "$extern:")
return;
@@ -2345,7 +2360,7 @@ void RTLIL::Cell::check()
void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed)
{
- if (type.substr(0, 1) != "$" || type.substr(0, 2) == "$_" || type.substr(0, 8) == "$paramod" ||
+ if (type.substr(0, 1) != "$" || type.substr(0, 2) == "$_" || type.substr(0, 8) == "$paramod" || type.substr(0,10) == "$fmcombine" ||
type.substr(0, 9) == "$verific$" || type.substr(0, 7) == "$array:" || type.substr(0, 8) == "$extern:")
return;
@@ -2397,6 +2412,9 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed)
if (connections_.count("\\Y"))
parameters["\\Y_WIDTH"] = GetSize(connections_["\\Y"]);
+ if (connections_.count("\\Q"))
+ parameters["\\WIDTH"] = GetSize(connections_["\\Q"]);
+
check();
}
@@ -3219,7 +3237,7 @@ void RTLIL::SigSpec::extend_u0(int width, bool is_signed)
remove(width, width_ - width);
if (width_ < width) {
- RTLIL::SigBit padding = width_ > 0 ? (*this)[width_ - 1] : RTLIL::State::S0;
+ RTLIL::SigBit padding = width_ > 0 ? (*this)[width_ - 1] : RTLIL::State::Sx;
if (!is_signed)
padding = RTLIL::State::S0;
while (width_ < width)
@@ -3780,6 +3798,11 @@ RTLIL::CaseRule::~CaseRule()
delete *it;
}
+bool RTLIL::CaseRule::empty() const
+{
+ return actions.empty() && switches.empty();
+}
+
RTLIL::CaseRule *RTLIL::CaseRule::clone() const
{
RTLIL::CaseRule *new_caserule = new RTLIL::CaseRule;
@@ -3796,6 +3819,11 @@ RTLIL::SwitchRule::~SwitchRule()
delete *it;
}
+bool RTLIL::SwitchRule::empty() const
+{
+ return cases.empty();
+}
+
RTLIL::SwitchRule *RTLIL::SwitchRule::clone() const
{
RTLIL::SwitchRule *new_switchrule = new RTLIL::SwitchRule;
diff --git a/kernel/rtlil.h b/kernel/rtlil.h
index 027faf41..52496e70 100644
--- a/kernel/rtlil.h
+++ b/kernel/rtlil.h
@@ -76,6 +76,9 @@ namespace RTLIL
struct IdString
{
+ #undef YOSYS_XTRACE_GET_PUT
+ #undef YOSYS_SORT_ID_FREE_LIST
+
// the global id string cache
static struct destruct_guard_t {
@@ -89,9 +92,43 @@ namespace RTLIL
static dict<char*, int, hash_cstr_ops> global_id_index_;
static std::vector<int> global_free_idx_list_;
+ static int last_created_idx_ptr_;
+ static int last_created_idx_[8];
+
+ static inline void xtrace_db_dump()
+ {
+ #ifdef YOSYS_XTRACE_GET_PUT
+ for (int idx = 0; idx < GetSize(global_id_storage_); idx++)
+ {
+ if (global_id_storage_.at(idx) == nullptr)
+ log("#X# DB-DUMP index %d: FREE\n", idx);
+ else
+ log("#X# DB-DUMP index %d: '%s' (ref %d)\n", idx, global_id_storage_.at(idx), global_refcount_storage_.at(idx));
+ }
+ #endif
+ }
+
+ static inline void checkpoint()
+ {
+ last_created_idx_ptr_ = 0;
+ for (int i = 0; i < 8; i++) {
+ if (last_created_idx_[i])
+ put_reference(last_created_idx_[i]);
+ last_created_idx_[i] = 0;
+ }
+ #ifdef YOSYS_SORT_ID_FREE_LIST
+ std::sort(global_free_idx_list_.begin(), global_free_idx_list_.end(), std::greater<int>());
+ #endif
+ }
+
static inline int get_reference(int idx)
{
global_refcount_storage_.at(idx)++;
+ #ifdef YOSYS_XTRACE_GET_PUT
+ if (yosys_xtrace) {
+ log("#X# GET-BY-INDEX '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
+ }
+ #endif
return idx;
}
@@ -107,6 +144,11 @@ namespace RTLIL
auto it = global_id_index_.find((char*)p);
if (it != global_id_index_.end()) {
global_refcount_storage_.at(it->second)++;
+ #ifdef YOSYS_XTRACE_GET_PUT
+ if (yosys_xtrace) {
+ log("#X# GET-BY-NAME '%s' (index %d, refcount %d)\n", global_id_storage_.at(it->second), it->second, global_refcount_storage_.at(it->second));
+ }
+ #endif
return it->second;
}
@@ -124,16 +166,22 @@ namespace RTLIL
global_refcount_storage_.at(idx)++;
// Avoid Create->Delete->Create pattern
- static IdString last_created_id;
- put_reference(last_created_id.index_);
- last_created_id.index_ = idx;
- get_reference(last_created_id.index_);
+ if (last_created_idx_[last_created_idx_ptr_])
+ put_reference(last_created_idx_[last_created_idx_ptr_]);
+ last_created_idx_[last_created_idx_ptr_] = idx;
+ get_reference(last_created_idx_[last_created_idx_ptr_]);
+ last_created_idx_ptr_ = (last_created_idx_ptr_ + 1) & 7;
if (yosys_xtrace) {
log("#X# New IdString '%s' with index %d.\n", p, idx);
log_backtrace("-X- ", yosys_xtrace-1);
}
+ #ifdef YOSYS_XTRACE_GET_PUT
+ if (yosys_xtrace) {
+ log("#X# GET-BY-NAME '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
+ }
+ #endif
return idx;
}
@@ -144,6 +192,12 @@ namespace RTLIL
if (!destruct_guard.ok)
return;
+ #ifdef YOSYS_XTRACE_GET_PUT
+ if (yosys_xtrace) {
+ log("#X# PUT '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
+ }
+ #endif
+
log_assert(global_refcount_storage_.at(idx) > 0);
if (--global_refcount_storage_.at(idx) != 0)
@@ -492,6 +546,14 @@ struct RTLIL::Const
return ret;
}
+ void extu(int width) {
+ bits.resize(width, RTLIL::State::S0);
+ }
+
+ void exts(int width) {
+ bits.resize(width, bits.empty() ? RTLIL::State::Sx : bits.back());
+ }
+
inline unsigned int hash() const {
unsigned int h = mkhash_init;
for (auto b : bits)
@@ -907,7 +969,9 @@ public:
Module();
virtual ~Module();
virtual RTLIL::IdString derive(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Const> parameters, bool mayfail = false);
+ virtual 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 = false);
virtual size_t count_id(RTLIL::IdString id);
+ virtual void reprocess_module(RTLIL::Design *design, dict<RTLIL::IdString, RTLIL::Module *> local_interfaces);
virtual void sort();
virtual void check();
@@ -1225,6 +1289,8 @@ struct RTLIL::CaseRule
~CaseRule();
void optimize();
+ bool empty() const;
+
template<typename T> void rewrite_sigspecs(T &functor);
RTLIL::CaseRule *clone() const;
};
@@ -1236,6 +1302,8 @@ struct RTLIL::SwitchRule : public RTLIL::AttrObject
~SwitchRule();
+ bool empty() const;
+
template<typename T> void rewrite_sigspecs(T &functor);
RTLIL::SwitchRule *clone() const;
};
@@ -1276,7 +1344,7 @@ inline bool RTLIL::SigBit::operator<(const RTLIL::SigBit &other) const {
return wire ? (offset < other.offset) : (data < other.data);
if (wire != nullptr && other.wire != nullptr)
return wire->name < other.wire->name;
- return wire < other.wire;
+ return (wire != nullptr) < (other.wire != nullptr);
}
inline bool RTLIL::SigBit::operator==(const RTLIL::SigBit &other) const {
diff --git a/kernel/yosys.cc b/kernel/yosys.cc
index ad032899..450e4e4c 100644
--- a/kernel/yosys.cc
+++ b/kernel/yosys.cc
@@ -33,7 +33,7 @@
# include <dlfcn.h>
#endif
-#ifdef _WIN32
+#if defined(_WIN32)
# include <windows.h>
# include <io.h>
#elif defined(__APPLE__)
@@ -41,13 +41,15 @@
# include <unistd.h>
# include <dirent.h>
# include <sys/stat.h>
-# include <glob.h>
#else
# include <unistd.h>
# include <dirent.h>
# include <sys/types.h>
# include <sys/wait.h>
# include <sys/stat.h>
+#endif
+
+#if !defined(_WIN32) && defined(YOSYS_ENABLE_GLOB)
# include <glob.h>
#endif
@@ -166,7 +168,7 @@ std::string vstringf(const char *fmt, va_list ap)
std::string string;
char *str = NULL;
-#if defined(_WIN32 )|| defined(__CYGWIN__)
+#if defined(_WIN32 )|| defined(__CYGWIN__)
int sz = 64, rc;
while (1) {
va_list apc;
@@ -216,12 +218,18 @@ std::string next_token(std::string &text, const char *sep, bool long_strings)
if (long_strings && pos_begin != text.size() && text[pos_begin] == '"') {
string sep_string = sep;
- for (size_t i = pos_begin+1; i < text.size(); i++)
+ for (size_t i = pos_begin+1; i < text.size(); i++) {
if (text[i] == '"' && (i+1 == text.size() || sep_string.find(text[i+1]) != std::string::npos)) {
std::string token = text.substr(pos_begin, i-pos_begin+1);
text = text.substr(i+1);
return token;
}
+ if (i+1 < text.size() && text[i] == '"' && text[i+1] == ';' && (i+2 == text.size() || sep_string.find(text[i+2]) != std::string::npos)) {
+ std::string token = text.substr(pos_begin, i-pos_begin+1);
+ text = text.substr(i+2);
+ return token + ";";
+ }
+ }
}
size_t pos_end = text.find_first_of(sep, pos_begin);
@@ -564,7 +572,7 @@ std::vector<std::string> glob_filename(const std::string &filename_pattern)
{
std::vector<std::string> results;
-#ifdef _WIN32
+#if defined(_WIN32) || !defined(YOSYS_ENABLE_GLOB)
results.push_back(filename_pattern);
#else
glob_t globbuf;
@@ -637,8 +645,9 @@ extern Tcl_Interp *yosys_get_tcl_interp()
struct TclPass : public Pass {
TclPass() : Pass("tcl", "execute a TCL script file") { }
void help() YS_OVERRIDE {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" tcl <filename>\n");
+ log(" tcl <filename> [args]\n");
log("\n");
log("This command executes the tcl commands in the specified file.\n");
log("Use 'yosys cmd' to run the yosys command 'cmd' from tcl.\n");
@@ -648,14 +657,24 @@ struct TclPass : public Pass {
log("'proc' and 'rename' are wrapped to tcl commands 'procs' and 'renames'\n");
log("in order to avoid a name collision with the built in commands.\n");
log("\n");
+ log("If any arguments are specified, these arguments are provided to the script via\n");
+ log("the standard $argc and $argv variables.\n");
+ log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE {
+ void execute(std::vector<std::string> args, RTLIL::Design *) YS_OVERRIDE {
if (args.size() < 2)
log_cmd_error("Missing script file.\n");
- if (args.size() > 2)
- extra_args(args, 1, design, false);
- if (Tcl_EvalFile(yosys_get_tcl_interp(), args[1].c_str()) != TCL_OK)
- log_cmd_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(yosys_get_tcl_interp()));
+
+ std::vector<Tcl_Obj*> script_args;
+ for (auto it = args.begin() + 2; it != args.end(); ++it)
+ script_args.push_back(Tcl_NewStringObj((*it).c_str(), (*it).size()));
+
+ Tcl_Interp *interp = yosys_get_tcl_interp();
+ Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argc", 4), NULL, Tcl_NewIntObj(script_args.size()), 0);
+ Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv", 4), NULL, Tcl_NewListObj(script_args.size(), script_args.data()), 0);
+ Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv0", 5), NULL, Tcl_NewStringObj(args[1].c_str(), args[1].size()), 0);
+ if (Tcl_EvalFile(interp, args[1].c_str()) != TCL_OK)
+ log_cmd_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(interp));
}
} TclPass;
#endif
@@ -733,7 +752,7 @@ std::string proc_self_dirname()
return "/";
}
#else
- #error Dont know how to determine process executable base path!
+ #error "Don't know how to determine process executable base path!"
#endif
#ifdef EMSCRIPTEN
@@ -799,7 +818,7 @@ static void handle_label(std::string &command, bool &from_to_active, const std::
while (pos < GetSize(command) && command[pos] != ' ' && command[pos] != '\t' && command[pos] != '\r' && command[pos] != '\n')
label += command[pos++];
- if (label.back() == ':' && GetSize(label) > 1)
+ if (GetSize(label) > 1 && label.back() == ':')
{
label = label.substr(0, GetSize(label)-1);
command = command.substr(pos);
@@ -821,7 +840,7 @@ void run_frontend(std::string filename, std::string command, std::string *backen
command = "verilog";
else if (filename.size() > 2 && filename.substr(filename.size()-3) == ".sv")
command = "verilog -sv";
- else if (filename.size() > 2 && filename.substr(filename.size()-4) == ".vhd")
+ else if (filename.size() > 3 && filename.substr(filename.size()-4) == ".vhd")
command = "vhdl";
else if (filename.size() > 4 && filename.substr(filename.size()-5) == ".blif")
command = "blif";
@@ -833,7 +852,7 @@ void run_frontend(std::string filename, std::string command, std::string *backen
command = "ilang";
else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".ys")
command = "script";
- else if (filename.size() > 2 && filename.substr(filename.size()-4) == ".tcl")
+ else if (filename.size() > 3 && filename.substr(filename.size()-4) == ".tcl")
command = "tcl";
else if (filename == "-")
command = "script";
diff --git a/libs/ezsat/ezminisat.h b/libs/ezsat/ezminisat.h
index 983e6fd0..3a34c13c 100644
--- a/libs/ezsat/ezminisat.h
+++ b/libs/ezsat/ezminisat.h
@@ -28,7 +28,7 @@
#include <time.h>
// minisat is using limit macros and format macros in their headers that
-// can be the source of some troubles when used from c++11. thefore we
+// can be the source of some troubles when used from c++11. therefore we
// don't force ezSAT users to use minisat headers..
namespace Minisat {
class Solver;
diff --git a/libs/subcircuit/README b/libs/subcircuit/README
index b1335681..ecaa987d 100644
--- a/libs/subcircuit/README
+++ b/libs/subcircuit/README
@@ -109,7 +109,7 @@ look at the demo.cc example program in this directory.
Setting up graphs
-----------------
-Instanciate the SubCircuit::Graph class and use the methods of this class to
+Instantiate the SubCircuit::Graph class and use the methods of this class to
set up the circuit.
SubCircuit::Graph myGraph;
@@ -152,7 +152,7 @@ rotate shift,
The method createConstant() can be used to add a constant driver to a signal.
The signal value is encoded as one char by bit, allowing for multi-valued
-logic matching. The follwoing command sets the lowest bit of cell6.A to a
+logic matching. The following command sets the lowest bit of cell6.A to a
logic 1:
myGraph.createConnection("cell6", "A", 0, '1');
@@ -314,7 +314,7 @@ bool userCompareEdge(needleGraphId, needleFromNodeId, needleFromUserData, needle
Perform additional checks on a pair of a pair of adjacent nodes (one
adjacent pair from the needle and one adjacent pair from the haystack)
- to determine wheter this edge from the needle is compatible with
+ to determine whether this edge from the needle is compatible with
that edge from the haystack. The default implementation always
returns true.
diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex
index 277e8932..e6491918 100644
--- a/manual/CHAPTER_CellLib.tex
+++ b/manual/CHAPTER_CellLib.tex
@@ -119,6 +119,12 @@ than one bit from \B{S} is set the output is undefined. Cells of this type are u
``parallel cases'' (defined by using the {\tt parallel\_case} attribute or detected by
an optimization).
+The {\tt \$tribuf} cell is used to implement tristate logic. Cells of this type have a \B{WIDTH}
+parameter and inputs \B{A} and \B{EN} and an output \B{Y}. The \B{A} input and \B{Y} output are
+\B{WIDTH} bits wide, and the \B{EN} input is one bit wide. When \B{EN} is 0, the output \B{Y}
+is not driven. When \B{EN} is 1, the value from \B{A} input is sent to the \B{Y} output. Therefore,
+the {\tt \$tribuf} cell implements the function \lstinline[language=Verilog]; Y = EN ? A : 'bz;.
+
Behavioural code with cascaded {\tt if-then-else}- and {\tt case}-statements
usually results in trees of multiplexer cells. Many passes (from various
optimizations to FSM extraction) heavily depend on these multiplexer trees to
@@ -211,14 +217,15 @@ Add information about {\tt \$sr} cells (set-reset flip-flops) and d-type latches
\subsection{Memories}
\label{sec:memcells}
-Memories are either represented using RTLIL::Memory objects and {\tt \$memrd} and {\tt \$memwr} cells
-or simply by using {\tt \$mem} cells.
+Memories are either represented using RTLIL::Memory objects, {\tt \$memrd}, {\tt \$memwr}, and {\tt \$meminit}
+cells, or by {\tt \$mem} cells alone.
In the first alternative the RTLIL::Memory objects hold the general metadata for the memory (bit width,
size in number of words, etc.) and for each port a {\tt \$memrd} (read port) or {\tt \$memwr} (write port)
cell is created. Having individual cells for read and write ports has the advantage that they can be
consolidated using resource sharing passes. In some cases this drastically reduces the number of required
-ports on the memory cell.
+ports on the memory cell. In this alternative, memory initialization data is represented by {\tt \$meminit} cells,
+which allow delaying constant folding for initialization addresses and data until after the frontend finishes.
The {\tt \$memrd} cells have a clock input \B{CLK}, an enable input \B{EN}, an
address input \B{ADDR}, and a data output \B{DATA}. They also have the
@@ -253,7 +260,7 @@ enable bit for each data bit), an address input \B{ADDR} and a data input
\begin{itemize}
\item \B{MEMID} \\
-The name of the RTLIL::Memory object that is associated with this read port.
+The name of the RTLIL::Memory object that is associated with this write port.
\item \B{ABITS} \\
The number of address bits (width of the \B{ADDR} input port).
@@ -262,7 +269,7 @@ The number of address bits (width of the \B{ADDR} input port).
The number of data bits (width of the \B{DATA} output port).
\item \B{CLK\_ENABLE} \\
-When this parameter is non-zero, the clock is used. Otherwise this read port is asynchronous and
+When this parameter is non-zero, the clock is used. Otherwise this write port is asynchronous and
the \B{CLK} input is not used.
\item \B{CLK\_POLARITY} \\
@@ -273,6 +280,27 @@ edge if this parameter is {\tt 1'b0}.
The cell with the higher integer value in this parameter wins a write conflict.
\end{itemize}
+The {\tt \$meminit} cells have an address input \B{ADDR} and a data input \B{DATA}, with the width
+of the \B{DATA} port equal to \B{WIDTH} parameter times \B{WORDS} parameter. Both of the inputs
+must resolve to a constant for synthesis to succeed.
+
+\begin{itemize}
+\item \B{MEMID} \\
+The name of the RTLIL::Memory object that is associated with this initialization cell.
+
+\item \B{ABITS} \\
+The number of address bits (width of the \B{ADDR} input port).
+
+\item \B{WIDTH} \\
+The number of data bits per memory location.
+
+\item \B{WORDS} \\
+The number of consecutive memory locations initialized by this cell.
+
+\item \B{PRIORITY} \\
+The cell with the higher integer value in this parameter wins an initialization conflict.
+\end{itemize}
+
The HDL frontend models a memory using RTLIL::Memory objects and asynchronous
{\tt \$memrd} and {\tt \$memwr} cells. The {\tt memory} pass (i.e.~its various sub-passes) migrates
{\tt \$dff} cells into the {\tt \$memrd} and {\tt \$memwr} cells making them synchronous, then
@@ -295,6 +323,9 @@ The number of address bits.
\item \B{WIDTH} \\
The number of data bits per word.
+\item \B{INIT} \\
+The initial memory contents.
+
\item \B{RD\_PORTS} \\
The number of read ports on this memory cell.
@@ -345,9 +376,11 @@ This input is \B{WR\_PORTS}*\B{ABITS} bits wide, containing all address signals
This input is \B{WR\_PORTS}*\B{WIDTH} bits wide, containing all data signals for the write ports.
\end{itemize}
-The {\tt techmap} pass can be used to manually map {\tt \$mem} cells to
-specialized memory cells on the target architecture, such as block ram resources
-on an FPGA.
+The {\tt memory\_collect} pass can be used to convert discrete {\tt \$memrd}, {\tt \$memwr}, and {\tt \$meminit} cells
+belonging to the same memory to a single {\tt \$mem} cell, whereas the {\tt memory\_unpack} pass performs the inverse operation.
+The {\tt memory\_dff} pass can combine asynchronous memory ports that are fed by or feeding registers into synchronous memory ports.
+The {\tt memory\_bram} pass can be used to recognize {\tt \$mem} cells that can be implemented with a block RAM resource on an FPGA.
+The {\tt memory\_map} pass can be used to implement {\tt \$mem} cells as basic logic: word-wide DFFs and address decoders.
\subsection{Finite State Machines}
@@ -371,9 +404,15 @@ Verilog & Cell Type \\
\hline
\lstinline[language=Verilog]; Y = ~A; & {\tt \$\_NOT\_} \\
\lstinline[language=Verilog]; Y = A & B; & {\tt \$\_AND\_} \\
+\lstinline[language=Verilog]; Y = ~(A & B); & {\tt \$\_NAND\_} \\
+\lstinline[language=Verilog]; Y = A & ~B; & {\tt \$\_ANDNOT\_} \\
\lstinline[language=Verilog]; Y = A | B; & {\tt \$\_OR\_} \\
+\lstinline[language=Verilog]; Y = ~(A | B); & {\tt \$\_NOR\_} \\
+\lstinline[language=Verilog]; Y = A | ~B; & {\tt \$\_ORNOT\_} \\
\lstinline[language=Verilog]; Y = A ^ B; & {\tt \$\_XOR\_} \\
+\lstinline[language=Verilog]; Y = ~(A ^ B); & {\tt \$\_XNOR\_} \\
\lstinline[language=Verilog]; Y = S ? B : A; & {\tt \$\_MUX\_} \\
+\lstinline[language=Verilog]; Y = EN ? A : 'bz; & {\tt \$\_TBUF\_} \\
\hline
\lstinline[language=Verilog]; always @(negedge C) Q <= D; & {\tt \$\_DFF\_N\_} \\
\lstinline[language=Verilog]; always @(posedge C) Q <= D; & {\tt \$\_DFF\_P\_} \\
@@ -396,9 +435,10 @@ $ClkEdge$ & $RstLvl$ & $RstVal$ & Cell Type \\
\end{table}
Table~\ref{tab:CellLib_gates} lists all cell types used for gate level logic. The cell types
-{\tt \$\_NOT\_}, {\tt \$\_AND\_}, {\tt \$\_OR\_}, {\tt \$\_XOR\_} and {\tt \$\_MUX\_}
-are used to model combinatorial logic. The cell types {\tt \$\_DFF\_N\_} and {\tt \$\_DFF\_P\_}
-represent d-type flip-flops.
+{\tt \$\_NOT\_}, {\tt \$\_AND\_}, {\tt \$\_NAND\_}, {\tt \$\_ANDNOT\_}, {\tt \$\_OR\_}, {\tt \$\_NOR\_},
+{\tt \$\_ORNOT\_}, {\tt \$\_XOR\_}, {\tt \$\_XNOR\_} and {\tt \$\_MUX\_} are used to model combinatorial logic.
+The cell type {\tt \$\_TBUF\_} is used to model tristate logic.
+The cell types {\tt \$\_DFF\_N\_} and {\tt \$\_DFF\_P\_} represent d-type flip-flops.
The cell types {\tt \$\_DFF\_NN0\_}, {\tt \$\_DFF\_NN1\_}, {\tt \$\_DFF\_NP0\_}, {\tt \$\_DFF\_NP1\_},
{\tt \$\_DFF\_PN0\_}, {\tt \$\_DFF\_PN1\_}, {\tt \$\_DFF\_PP0\_} and {\tt \$\_DFF\_PP1\_} implement
@@ -410,7 +450,7 @@ otherwise.
\begin{lstlisting}[mathescape,language=Verilog]
always @($ClkEdge$ C, $RstEdge$ R)
if (R == $RstLvl$)
- Q <= $RstVa$l;
+ Q <= $RstVal$;
else
Q <= D;
\end{lstlisting}
@@ -450,7 +490,6 @@ Add information about {\tt \$\_DFFE\_??\_}, {\tt \$\_DFFSR\_???\_}, {\tt \$\_DLA
\end{fixme}
\begin{fixme}
-Add information about {\tt \$\_NAND\_}, {\tt \$\_NOR\_}, {\tt \$\_XNOR\_}, {\tt \$\_ANDNOT\_}, {\tt \$\_ORNOT\_},
-{\tt \$\_AOI3\_}, {\tt \$\_OAI3\_}, {\tt \$\_AOI4\_}, and {\tt \$\_OAI4\_} cells.
+Add information about {\tt \$\_AOI3\_}, {\tt \$\_OAI3\_}, {\tt \$\_AOI4\_}, and {\tt \$\_OAI4\_} cells.
\end{fixme}
diff --git a/manual/CHAPTER_Overview.tex b/manual/CHAPTER_Overview.tex
index 964875d5..2feb0f1c 100644
--- a/manual/CHAPTER_Overview.tex
+++ b/manual/CHAPTER_Overview.tex
@@ -428,8 +428,8 @@ memory object has the following properties:
All read accesses to the memory are transformed to {\tt \$memrd} cells and all write accesses to
{\tt \$memwr} cells by the language frontend. These cells consist of independent read- and write-ports
-to the memory. The \B{MEMID} parameter on these cells is used to link them together and to the
-RTLIL::Memory object they belong to.
+to the memory. Memory initialization is transformed to {\tt \$meminit} cells by the language frontend.
+The \B{MEMID} parameter on these cells is used to link them together and to the RTLIL::Memory object they belong to.
The rationale behind using separate cells for the individual ports versus
creating a large multiport memory cell right in the language frontend is that
diff --git a/misc/launcher.c b/misc/launcher.c
new file mode 100644
index 00000000..157d68cf
--- /dev/null
+++ b/misc/launcher.c
@@ -0,0 +1,358 @@
+/* This file comes from the PyPA Setuptools repository, commit 16e452a:
+https://github.com/pypa/setuptools
+Modifications include this comment and inline inclusion of the LICENSE text. */
+
+/* Copyright (C) 2016 Jason R Coombs <jaraco@jaraco.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. */
+
+/* Setuptools Script Launcher for Windows
+
+ This is a stub executable for Windows that functions somewhat like
+ Effbot's "exemaker", in that it runs a script with the same name but
+ a .py extension, using information from a #! line. It differs in that
+ it spawns the actual Python executable, rather than attempting to
+ hook into the Python DLL. This means that the script will run with
+ sys.executable set to the Python executable, where exemaker ends up with
+ sys.executable pointing to itself. (Which means it won't work if you try
+ to run another Python process using sys.executable.)
+
+ To build/rebuild with mingw32, do this in the setuptools project directory:
+
+ gcc -DGUI=0 -mno-cygwin -O -s -o setuptools/cli.exe launcher.c
+ gcc -DGUI=1 -mwindows -mno-cygwin -O -s -o setuptools/gui.exe launcher.c
+
+ To build for Windows RT, install both Visual Studio Express for Windows 8
+ and for Windows Desktop (both freeware), create "win32" application using
+ "Windows Desktop" version, create new "ARM" target via
+ "Configuration Manager" menu and modify ".vcxproj" file by adding
+ "<WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>" tag
+ as child of "PropertyGroup" tags that has "Debug|ARM" and "Release|ARM"
+ properties.
+
+ It links to msvcrt.dll, but this shouldn't be a problem since it doesn't
+ actually run Python in the same process. Note that using 'exec' instead
+ of 'spawn' doesn't work, because on Windows this leads to the Python
+ executable running in the *background*, attached to the same console
+ window, meaning you get a command prompt back *before* Python even finishes
+ starting. So, we have to use spawnv() and wait for Python to exit before
+ continuing. :(
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <windows.h>
+#include <tchar.h>
+#include <fcntl.h>
+
+int child_pid=0;
+
+int fail(char *format, char *data) {
+ /* Print error message to stderr and return 2 */
+ fprintf(stderr, format, data);
+ return 2;
+}
+
+char *quoted(char *data) {
+ int i, ln = strlen(data), nb;
+
+ /* We allocate twice as much space as needed to deal with worse-case
+ of having to escape everything. */
+ char *result = calloc(ln*2+3, sizeof(char));
+ char *presult = result;
+
+ *presult++ = '"';
+ for (nb=0, i=0; i < ln; i++)
+ {
+ if (data[i] == '\\')
+ nb += 1;
+ else if (data[i] == '"')
+ {
+ for (; nb > 0; nb--)
+ *presult++ = '\\';
+ *presult++ = '\\';
+ }
+ else
+ nb = 0;
+ *presult++ = data[i];
+ }
+
+ for (; nb > 0; nb--) /* Deal w trailing slashes */
+ *presult++ = '\\';
+
+ *presult++ = '"';
+ *presult++ = 0;
+ return result;
+}
+
+
+
+
+
+
+
+
+
+
+char *loadable_exe(char *exename) {
+ /* HINSTANCE hPython; DLL handle for python executable */
+ char *result;
+
+ /* hPython = LoadLibraryEx(exename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+ if (!hPython) return NULL; */
+
+ /* Return the absolute filename for spawnv */
+ result = calloc(MAX_PATH, sizeof(char));
+ strncpy(result, exename, MAX_PATH);
+ /*if (result) GetModuleFileNameA(hPython, result, MAX_PATH);
+
+ FreeLibrary(hPython); */
+ return result;
+}
+
+
+char *find_exe(char *exename, char *script) {
+ char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
+ char path[_MAX_PATH], c, *result;
+
+ /* convert slashes to backslashes for uniform search below */
+ result = exename;
+ while (c = *result++) if (c=='/') result[-1] = '\\';
+
+ _splitpath(exename, drive, dir, fname, ext);
+ if (drive[0] || dir[0]=='\\') {
+ return loadable_exe(exename); /* absolute path, use directly */
+ }
+ /* Use the script's parent directory, which should be the Python home
+ (This should only be used for bdist_wininst-installed scripts, because
+ easy_install-ed scripts use the absolute path to python[w].exe
+ */
+ _splitpath(script, drive, dir, fname, ext);
+ result = dir + strlen(dir) -1;
+ if (*result == '\\') result--;
+ while (*result != '\\' && result>=dir) *result-- = 0;
+ _makepath(path, drive, dir, exename, NULL);
+ return loadable_exe(path);
+}
+
+
+char **parse_argv(char *cmdline, int *argc)
+{
+ /* Parse a command line in-place using MS C rules */
+
+ char **result = calloc(strlen(cmdline), sizeof(char *));
+ char *output = cmdline;
+ char c;
+ int nb = 0;
+ int iq = 0;
+ *argc = 0;
+
+ result[0] = output;
+ while (isspace(*cmdline)) cmdline++; /* skip leading spaces */
+
+ do {
+ c = *cmdline++;
+ if (!c || (isspace(c) && !iq)) {
+ while (nb) {*output++ = '\\'; nb--; }
+ *output++ = 0;
+ result[++*argc] = output;
+ if (!c) return result;
+ while (isspace(*cmdline)) cmdline++; /* skip leading spaces */
+ if (!*cmdline) return result; /* avoid empty arg if trailing ws */
+ continue;
+ }
+ if (c == '\\')
+ ++nb; /* count \'s */
+ else {
+ if (c == '"') {
+ if (!(nb & 1)) { iq = !iq; c = 0; } /* skip " unless odd # of \ */
+ nb = nb >> 1; /* cut \'s in half */
+ }
+ while (nb) {*output++ = '\\'; nb--; }
+ if (c) *output++ = c;
+ }
+ } while (1);
+}
+
+void pass_control_to_child(DWORD control_type) {
+ /*
+ * distribute-issue207
+ * passes the control event to child process (Python)
+ */
+ if (!child_pid) {
+ return;
+ }
+ GenerateConsoleCtrlEvent(child_pid,0);
+}
+
+BOOL control_handler(DWORD control_type) {
+ /*
+ * distribute-issue207
+ * control event handler callback function
+ */
+ switch (control_type) {
+ case CTRL_C_EVENT:
+ pass_control_to_child(0);
+ break;
+ }
+ return TRUE;
+}
+
+int create_and_wait_for_subprocess(char* command) {
+ /*
+ * distribute-issue207
+ * launches child process (Python)
+ */
+ DWORD return_value = 0;
+ LPSTR commandline = command;
+ STARTUPINFOA s_info;
+ PROCESS_INFORMATION p_info;
+ ZeroMemory(&p_info, sizeof(p_info));
+ ZeroMemory(&s_info, sizeof(s_info));
+ s_info.cb = sizeof(STARTUPINFO);
+ // set-up control handler callback funciotn
+ SetConsoleCtrlHandler((PHANDLER_ROUTINE) control_handler, TRUE);
+ if (!CreateProcessA(NULL, commandline, NULL, NULL, TRUE, 0, NULL, NULL, &s_info, &p_info)) {
+ fprintf(stderr, "failed to create process.\n");
+ return 0;
+ }
+ child_pid = p_info.dwProcessId;
+ // wait for Python to exit
+ WaitForSingleObject(p_info.hProcess, INFINITE);
+ if (!GetExitCodeProcess(p_info.hProcess, &return_value)) {
+ fprintf(stderr, "failed to get exit code from process.\n");
+ return 0;
+ }
+ return return_value;
+}
+
+char* join_executable_and_args(char *executable, char **args, int argc)
+{
+ /*
+ * distribute-issue207
+ * CreateProcess needs a long string of the executable and command-line arguments,
+ * so we need to convert it from the args that was built
+ */
+ int len,counter;
+ char* cmdline;
+
+ len=strlen(executable)+2;
+ for (counter=1; counter<argc; counter++) {
+ len+=strlen(args[counter])+1;
+ }
+
+ cmdline = (char*)calloc(len, sizeof(char));
+ sprintf(cmdline, "%s", executable);
+ len=strlen(executable);
+ for (counter=1; counter<argc; counter++) {
+ sprintf(cmdline+len, " %s", args[counter]);
+ len+=strlen(args[counter])+1;
+ }
+ return cmdline;
+}
+
+int run(int argc, char **argv, int is_gui) {
+
+ char python[256]; /* python executable's filename*/
+ char *pyopt; /* Python option */
+ char script[256]; /* the script's filename */
+
+ int scriptf; /* file descriptor for script file */
+
+ char **newargs, **newargsp, **parsedargs; /* argument array for exec */
+ char *ptr, *end; /* working pointers for string manipulation */
+ char *cmdline;
+ int i, parsedargc; /* loop counter */
+
+ /* compute script name from our .exe name*/
+ GetModuleFileNameA(NULL, script, sizeof(script));
+ end = script + strlen(script);
+ while( end>script && *end != '.')
+ *end-- = '\0';
+ *end-- = '\0';
+ strcat(script, (GUI ? "-script.pyw" : "-script.py"));
+
+ /* figure out the target python executable */
+
+ scriptf = open(script, O_RDONLY);
+ if (scriptf == -1) {
+ return fail("Cannot open %s\n", script);
+ }
+ end = python + read(scriptf, python, sizeof(python));
+ close(scriptf);
+
+ ptr = python-1;
+ while(++ptr < end && *ptr && *ptr!='\n' && *ptr!='\r') {;}
+
+ *ptr-- = '\0';
+
+ if (strncmp(python, "#!", 2)) {
+ /* default to python.exe if no #! header */
+ strcpy(python, "#!python.exe");
+ }
+
+ parsedargs = parse_argv(python+2, &parsedargc);
+
+ /* Using spawnv() can fail strangely if you e.g. find the Cygwin
+ Python, so we'll make sure Windows can find and load it */
+
+ ptr = find_exe(parsedargs[0], script);
+ if (!ptr) {
+ return fail("Cannot find Python executable %s\n", parsedargs[0]);
+ }
+
+ /* printf("Python executable: %s\n", ptr); */
+
+ /* Argument array needs to be
+ parsedargc + argc, plus 1 for null sentinel */
+
+ newargs = (char **)calloc(parsedargc + argc + 1, sizeof(char *));
+ newargsp = newargs;
+
+ *newargsp++ = quoted(ptr);
+ for (i = 1; i<parsedargc; i++) *newargsp++ = quoted(parsedargs[i]);
+
+ *newargsp++ = quoted(script);
+ for (i = 1; i < argc; i++) *newargsp++ = quoted(argv[i]);
+
+ *newargsp++ = NULL;
+
+ /* printf("args 0: %s\nargs 1: %s\n", newargs[0], newargs[1]); */
+
+ if (is_gui) {
+ /* Use exec, we don't need to wait for the GUI to finish */
+ execv(ptr, (const char * const *)(newargs));
+ return fail("Could not exec %s", ptr); /* shouldn't get here! */
+ }
+
+ /*
+ * distribute-issue207: using CreateProcessA instead of spawnv
+ */
+ cmdline = join_executable_and_args(ptr, newargs, parsedargc + argc);
+ return create_and_wait_for_subprocess(cmdline);
+}
+
+int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpCmd, int nShow) {
+ return run(__argc, __argv, GUI);
+}
+
+int main(int argc, char** argv) {
+ return run(argc, argv, GUI);
+}
diff --git a/misc/yosys.proto b/misc/yosys.proto
index 2870176c..a583e626 100644
--- a/misc/yosys.proto
+++ b/misc/yosys.proto
@@ -1,12 +1,12 @@
//
// yosys -- Yosys Open SYnthesis Suite
-//
+//
// Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
-//
+//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
-//
+//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
@@ -73,7 +73,7 @@ message Module {
BitVector bits = 2;
}
map<string, Port> port = 2;
-
+
// Named cells in this module.
message Cell {
// Set to true when the name of this cell is automatically created and
@@ -129,7 +129,7 @@ message Model {
TYPE_FALSE = 6;
};
Type type = 1;
-
+
message Port {
// Name of port.
string portname = 1;
@@ -148,7 +148,7 @@ message Model {
// Set for AND, NAND.
Gate gate = 3;
}
-
+
// Set when the node drives given output port(s).
message OutPort {
// Name of port.
diff --git a/passes/cmds/Makefile.inc b/passes/cmds/Makefile.inc
index 44a83b2b..c8067a8b 100644
--- a/passes/cmds/Makefile.inc
+++ b/passes/cmds/Makefile.inc
@@ -29,4 +29,4 @@ OBJS += passes/cmds/chformal.o
OBJS += passes/cmds/chtype.o
OBJS += passes/cmds/blackbox.o
OBJS += passes/cmds/ltp.o
-
+OBJS += passes/cmds/bugpoint.o
diff --git a/passes/cmds/bugpoint.cc b/passes/cmds/bugpoint.cc
new file mode 100644
index 00000000..606276e6
--- /dev/null
+++ b/passes/cmds/bugpoint.cc
@@ -0,0 +1,369 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2018 whitequark <whitequark@whitequark.org>
+ *
+ * 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 "backends/ilang/ilang_backend.h"
+
+USING_YOSYS_NAMESPACE
+using namespace ILANG_BACKEND;
+PRIVATE_NAMESPACE_BEGIN
+
+struct BugpointPass : public Pass {
+ BugpointPass() : Pass("bugpoint", "minimize testcases") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" bugpoint [options]\n");
+ log("\n");
+ log("This command minimizes testcases that crash Yosys. It removes an arbitrary part\n");
+ log("of the design and recursively invokes Yosys with a given script, repeating these\n");
+ log("steps while it can find a smaller design that still causes a crash. Once this\n");
+ log("command finishes, it replaces the current design with the smallest testcase it\n");
+ log("was able to produce.\n");
+ log("\n");
+ log("It is possible to specify the kinds of design part that will be removed. If none\n");
+ log("are specified, all parts of design will be removed.\n");
+ log("\n");
+ log(" -yosys <filename>\n");
+ log(" use this Yosys binary. if not specified, `yosys` is used.\n");
+ log("\n");
+ log(" -script <filename>\n");
+ log(" use this script to crash Yosys. required.\n");
+ log("\n");
+ log(" -grep <string>\n");
+ log(" only consider crashes that place this string in the log file.\n");
+ log("\n");
+ log(" -fast\n");
+ log(" run `clean -purge` after each minimization step. converges faster, but\n");
+ log(" produces larger testcases, and may fail to produce any testcase at all if\n");
+ log(" the crash is related to dangling wires.\n");
+ log("\n");
+ log(" -clean\n");
+ log(" run `clean -purge` before checking testcase and after finishing. produces\n");
+ log(" smaller and more useful testcases, but may fail to produce any testcase\n");
+ log(" at all if the crash is related to dangling wires.\n");
+ log("\n");
+ log(" -modules\n");
+ log(" try to remove modules.\n");
+ log("\n");
+ log(" -ports\n");
+ log(" try to remove module ports.\n");
+ log("\n");
+ log(" -cells\n");
+ log(" try to remove cells.\n");
+ log("\n");
+ log(" -connections\n");
+ log(" try to reconnect ports to 'x.\n");
+ log("\n");
+ }
+
+ bool run_yosys(RTLIL::Design *design, string yosys_cmd, string script)
+ {
+ design->sort();
+
+ std::ofstream f("bugpoint-case.il");
+ ILANG_BACKEND::dump_design(f, design, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false);
+ f.close();
+
+ string yosys_cmdline = stringf("%s -qq -L bugpoint-case.log -s %s bugpoint-case.il", yosys_cmd.c_str(), script.c_str());
+ return run_command(yosys_cmdline) == 0;
+ }
+
+ bool check_logfile(string grep)
+ {
+ if (grep.empty())
+ return true;
+
+ std::ifstream f("bugpoint-case.log");
+ while (!f.eof())
+ {
+ string line;
+ getline(f, line);
+ if (line.find(grep) != std::string::npos)
+ return true;
+ }
+ return false;
+ }
+
+ RTLIL::Design *clean_design(RTLIL::Design *design, bool do_clean = true, bool do_delete = false)
+ {
+ if (!do_clean)
+ return design;
+
+ RTLIL::Design *design_copy = new RTLIL::Design;
+ for (auto &it : design->modules_)
+ design_copy->add(it.second->clone());
+ Pass::call(design_copy, "clean -purge");
+
+ if (do_delete)
+ delete design;
+ return design_copy;
+ }
+
+ RTLIL::Design *simplify_something(RTLIL::Design *design, int &seed, bool stage2, bool modules, bool ports, bool cells, bool connections)
+ {
+ RTLIL::Design *design_copy = new RTLIL::Design;
+ for (auto &it : design->modules_)
+ design_copy->add(it.second->clone());
+
+ int index = 0;
+ if (modules)
+ {
+ for (auto &it : design_copy->modules_)
+ {
+ if (it.second->get_bool_attribute("\\blackbox"))
+ continue;
+
+ if (index++ == seed)
+ {
+ log("Trying to remove module %s.\n", it.first.c_str());
+ design_copy->remove(it.second);
+ return design_copy;
+ }
+ }
+ }
+ if (ports)
+ {
+ for (auto mod : design_copy->modules())
+ {
+ if (mod->get_bool_attribute("\\blackbox"))
+ continue;
+
+ for (auto wire : mod->wires())
+ {
+ if (!stage2 && wire->get_bool_attribute("$bugpoint"))
+ continue;
+
+ if (wire->port_input || wire->port_output)
+ {
+ if (index++ == seed)
+ {
+ log("Trying to remove module port %s.\n", log_signal(wire));
+ wire->port_input = wire->port_output = false;
+ mod->fixup_ports();
+ return design_copy;
+ }
+ }
+ }
+ }
+ }
+ if (cells)
+ {
+ for (auto mod : design_copy->modules())
+ {
+ if (mod->get_bool_attribute("\\blackbox"))
+ continue;
+
+ for (auto &it : mod->cells_)
+ {
+ if (index++ == seed)
+ {
+ log("Trying to remove cell %s.%s.\n", mod->name.c_str(), it.first.c_str());
+ mod->remove(it.second);
+ return design_copy;
+ }
+ }
+ }
+ }
+ if (connections)
+ {
+ for (auto mod : design_copy->modules())
+ {
+ if (mod->get_bool_attribute("\\blackbox"))
+ continue;
+
+ for (auto cell : mod->cells())
+ {
+ for (auto it : cell->connections_)
+ {
+ RTLIL::SigSpec port = cell->getPort(it.first);
+ bool is_undef = port.is_fully_undef();
+ bool is_port = port.is_wire() && (port.as_wire()->port_input || port.as_wire()->port_output);
+
+ if(is_undef || (!stage2 && is_port))
+ continue;
+
+ if (index++ == seed)
+ {
+ log("Trying to remove cell port %s.%s.%s.\n", mod->name.c_str(), cell->name.c_str(), it.first.c_str());
+ RTLIL::SigSpec port_x(State::Sx, port.size());
+ cell->unsetPort(it.first);
+ cell->setPort(it.first, port_x);
+ return design_copy;
+ }
+
+ if (!stage2 && (cell->input(it.first) || cell->output(it.first)) && index++ == seed)
+ {
+ log("Trying to expose cell port %s.%s.%s as module port.\n", mod->name.c_str(), cell->name.c_str(), it.first.c_str());
+ RTLIL::Wire *wire = mod->addWire(NEW_ID, port.size());
+ wire->set_bool_attribute("$bugpoint");
+ wire->port_input = cell->input(it.first);
+ wire->port_output = cell->output(it.first);
+ cell->unsetPort(it.first);
+ cell->setPort(it.first, wire);
+ mod->fixup_ports();
+ return design_copy;
+ }
+ }
+ }
+ }
+ }
+ return NULL;
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ string yosys_cmd = "yosys", script, grep;
+ bool fast = false, clean = false;
+ bool modules = false, ports = false, cells = false, connections = false, has_part = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-yosys" && argidx + 1 < args.size()) {
+ yosys_cmd = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-script" && argidx + 1 < args.size()) {
+ script = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-grep" && argidx + 1 < args.size()) {
+ grep = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-fast") {
+ fast = true;
+ continue;
+ }
+ if (args[argidx] == "-clean") {
+ clean = true;
+ continue;
+ }
+ if (args[argidx] == "-modules") {
+ modules = true;
+ has_part = true;
+ continue;
+ }
+ if (args[argidx] == "-ports") {
+ ports = true;
+ has_part = true;
+ continue;
+ }
+ if (args[argidx] == "-cells") {
+ cells = true;
+ has_part = true;
+ continue;
+ }
+ if (args[argidx] == "-connections") {
+ connections = true;
+ has_part = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!has_part)
+ {
+ modules = true;
+ ports = true;
+ cells = true;
+ connections = true;
+ }
+
+ if (!design->full_selection())
+ log_cmd_error("This command only operates on fully selected designs!\n");
+
+ RTLIL::Design *crashing_design = clean_design(design, clean);
+ if (run_yosys(crashing_design, yosys_cmd, script))
+ log_cmd_error("The provided script file and Yosys binary do not crash on this design!\n");
+ 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;
+ bool found_something = false, stage2 = false;
+ while (true)
+ {
+ if (RTLIL::Design *simplified = simplify_something(crashing_design, seed, stage2, modules, ports, cells, connections))
+ {
+ simplified = clean_design(simplified, fast, /*do_delete=*/true);
+
+ bool crashes;
+ if (clean)
+ {
+ RTLIL::Design *testcase = clean_design(simplified);
+ crashes = !run_yosys(testcase, yosys_cmd, script);
+ delete testcase;
+ }
+ else
+ {
+ crashes = !run_yosys(simplified, yosys_cmd, script);
+ }
+
+ if (crashes && check_logfile(grep))
+ {
+ log("Testcase crashes.\n");
+ if (crashing_design != design)
+ delete crashing_design;
+ crashing_design = simplified;
+ crashing_seed = seed;
+ found_something = true;
+ }
+ else
+ {
+ log("Testcase does not crash.\n");
+ delete simplified;
+ seed++;
+ }
+ }
+ else
+ {
+ seed = 0;
+ if (found_something)
+ found_something = false;
+ else
+ {
+ if (!stage2)
+ {
+ log("Demoting introduced module ports.\n");
+ stage2 = true;
+ }
+ else
+ {
+ log("Simplifications exhausted.\n");
+ break;
+ }
+ }
+ }
+ }
+
+ if (crashing_design != design)
+ {
+ Pass::call(design, "design -reset");
+ crashing_design = clean_design(crashing_design, clean, /*do_delete=*/true);
+ for (auto &it : crashing_design->modules_)
+ design->add(it.second->clone());
+ delete crashing_design;
+ }
+ }
+} BugpointPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc
index 522758ea..7e32da65 100644
--- a/passes/cmds/chformal.cc
+++ b/passes/cmds/chformal.cc
@@ -32,7 +32,7 @@ struct ChformalPass : public Pass {
log(" chformal [types] [mode] [options] [selection]\n");
log("\n");
log("Make changes to the formal constraints of the design. The [types] options\n");
- log("the type of constraint to operate on. If none of the folling options is given,\n");
+ log("the type of constraint to operate on. If none of the following options are given,\n");
log("the command will operate on all constraint types:\n");
log("\n");
log(" -assert $assert cells, representing assert(...) constraints\n");
@@ -59,7 +59,7 @@ struct ChformalPass : public Pass {
log(" -assume2assert\n");
log(" -live2fair\n");
log(" -fair2live\n");
- log(" change the roles of cells as indicated. this options can be combined\n");
+ log(" change the roles of cells as indicated. these options can be combined\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
diff --git a/passes/cmds/connect.cc b/passes/cmds/connect.cc
index d480b79a..f93bada2 100644
--- a/passes/cmds/connect.cc
+++ b/passes/cmds/connect.cc
@@ -137,7 +137,7 @@ struct ConnectPass : public Pass {
if (!set_lhs.empty())
{
if (!unset_expr.empty() || !port_cell.empty())
- log_cmd_error("Cant use -set together with -unset and/or -port.\n");
+ log_cmd_error("Can't use -set together with -unset and/or -port.\n");
RTLIL::SigSpec sig_lhs, sig_rhs;
if (!RTLIL::SigSpec::parse_sel(sig_lhs, design, module, set_lhs))
@@ -157,7 +157,7 @@ struct ConnectPass : public Pass {
if (!unset_expr.empty())
{
if (!port_cell.empty() || flag_nounset)
- log_cmd_error("Cant use -unset together with -port and/or -nounset.\n");
+ log_cmd_error("Can't use -unset together with -port and/or -nounset.\n");
RTLIL::SigSpec sig;
if (!RTLIL::SigSpec::parse_sel(sig, design, module, unset_expr))
@@ -170,7 +170,7 @@ struct ConnectPass : public Pass {
if (!port_cell.empty())
{
if (flag_nounset)
- log_cmd_error("Cant use -port together with -nounset.\n");
+ log_cmd_error("Can't use -port together with -nounset.\n");
if (module->cells_.count(RTLIL::escape_id(port_cell)) == 0)
log_cmd_error("Can't find cell %s.\n", port_cell.c_str());
diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc
index dce576fd..9b1830b7 100644
--- a/passes/cmds/rename.cc
+++ b/passes/cmds/rename.cc
@@ -24,7 +24,7 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name)
+static void rename_in_module(RTLIL::Module *module, std::string from_name, std::string to_name, bool flag_output)
{
from_name = RTLIL::escape_id(from_name);
to_name = RTLIL::escape_id(to_name);
@@ -37,13 +37,18 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std::
Wire *w = it.second;
log("Renaming wire %s to %s in module %s.\n", log_id(w), log_id(to_name), log_id(module));
module->rename(w, to_name);
- if (w->port_id)
+ if (w->port_id || flag_output) {
+ if (flag_output)
+ w->port_output = true;
module->fixup_ports();
+ }
return;
}
for (auto &it : module->cells_)
if (it.first == from_name) {
+ if (flag_output)
+ log_cmd_error("Called with -output but the specified object is a cell.\n");
log("Renaming cell %s to %s in module %s.\n", log_id(it.second), log_id(to_name), log_id(module));
module->rename(it.second, to_name);
return;
@@ -52,6 +57,51 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std::
log_cmd_error("Object `%s' not found!\n", from_name.c_str());
}
+static std::string derive_name_from_src(const std::string &src, int counter)
+{
+ std::string src_base = src.substr(0, src.find('|'));
+ if (src_base.empty())
+ return stringf("$%d", counter);
+ else
+ return stringf("\\%s$%d", src_base.c_str(), counter);
+}
+
+static IdString derive_name_from_wire(const RTLIL::Cell &cell)
+{
+ // Find output
+ const SigSpec *output = nullptr;
+ int num_outputs = 0;
+ for (auto &connection : cell.connections()) {
+ if (cell.output(connection.first)) {
+ output = &connection.second;
+ num_outputs++;
+ }
+ }
+
+ if (num_outputs != 1) // Skip cells thad drive multiple outputs
+ return cell.name;
+
+ std::string name = "";
+ for (auto &chunk : output->chunks()) {
+ // Skip cells that drive privately named wires
+ if (!chunk.wire || chunk.wire->name.str()[0] == '$')
+ return cell.name;
+
+ if (name != "")
+ name += "$";
+
+ name += chunk.wire->name.str();
+ if (chunk.wire->width != chunk.width) {
+ name += "[";
+ if (chunk.width != 1)
+ name += std::to_string(chunk.offset + chunk.width) + ":";
+ name += std::to_string(chunk.offset) + "]";
+ }
+ }
+
+ return name + cell.type.str();
+}
+
struct RenamePass : public Pass {
RenamePass() : Pass("rename", "rename object in the design") { }
void help() YS_OVERRIDE
@@ -64,6 +114,25 @@ struct RenamePass : public Pass {
log("by this command.\n");
log("\n");
log("\n");
+ log("\n");
+ log(" rename -output old_name new_name\n");
+ log("\n");
+ log("Like above, but also make the wire an output. This will fail if the object is\n");
+ log("not a wire.\n");
+ log("\n");
+ log("\n");
+ log(" rename -src [selection]\n");
+ log("\n");
+ log("Assign names auto-generated from the src attribute to all selected wires and\n");
+ log("cells with private names.\n");
+ log("\n");
+ log("\n");
+ log(" rename -wire [selection]\n");
+ log("\n");
+ log("Assign auto-generated names based on the wires they drive to all selected\n");
+ log("cells with private names. Ignores cells driving privatly named wires.\n");
+ log("\n");
+ log("\n");
log(" rename -enumerate [-pattern <pattern>] [selection]\n");
log("\n");
log("Assign short auto-generated names to all selected wires and cells with private\n");
@@ -71,11 +140,13 @@ struct RenamePass : public Pass {
log("The character %% in the pattern is replaced with a integer number. The default\n");
log("pattern is '_%%_'.\n");
log("\n");
+ log("\n");
log(" rename -hide [selection]\n");
log("\n");
log("Assign private names (the ones with $-prefix) to all selected wires and cells\n");
log("with public names. This ignores all selected ports.\n");
log("\n");
+ log("\n");
log(" rename -top new_name\n");
log("\n");
log("Rename top module.\n");
@@ -84,15 +155,33 @@ struct RenamePass : public Pass {
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::string pattern_prefix = "_", pattern_suffix = "_";
+ bool flag_src = false;
+ bool flag_wire = false;
bool flag_enumerate = false;
bool flag_hide = false;
bool flag_top = false;
+ bool flag_output = false;
bool got_mode = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
std::string arg = args[argidx];
+ if (arg == "-src" && !got_mode) {
+ flag_src = true;
+ got_mode = true;
+ continue;
+ }
+ if (arg == "-output" && !got_mode) {
+ flag_output = true;
+ got_mode = true;
+ continue;
+ }
+ if (arg == "-wire" && !got_mode) {
+ flag_wire = true;
+ got_mode = true;
+ continue;
+ }
if (arg == "-enumerate" && !got_mode) {
flag_enumerate = true;
got_mode = true;
@@ -117,6 +206,57 @@ struct RenamePass : public Pass {
break;
}
+ if (flag_src)
+ {
+ extra_args(args, argidx, design);
+
+ for (auto &mod : design->modules_)
+ {
+ int counter = 0;
+
+ RTLIL::Module *module = mod.second;
+ if (!design->selected(module))
+ continue;
+
+ dict<RTLIL::IdString, RTLIL::Wire*> new_wires;
+ for (auto &it : module->wires_) {
+ if (it.first[0] == '$' && design->selected(module, it.second))
+ it.second->name = derive_name_from_src(it.second->get_src_attribute(), counter++);
+ new_wires[it.second->name] = it.second;
+ }
+ module->wires_.swap(new_wires);
+ module->fixup_ports();
+
+ dict<RTLIL::IdString, RTLIL::Cell*> new_cells;
+ for (auto &it : module->cells_) {
+ if (it.first[0] == '$' && design->selected(module, it.second))
+ it.second->name = derive_name_from_src(it.second->get_src_attribute(), counter++);
+ new_cells[it.second->name] = it.second;
+ }
+ module->cells_.swap(new_cells);
+ }
+ }
+ else
+ if (flag_wire)
+ {
+ extra_args(args, argidx, design);
+
+ for (auto &mod : design->modules_)
+ {
+ RTLIL::Module *module = mod.second;
+ if (!design->selected(module))
+ continue;
+
+ dict<RTLIL::IdString, RTLIL::Cell*> new_cells;
+ for (auto &it : module->cells_) {
+ if (it.first[0] == '$' && design->selected(module, it.second))
+ it.second->name = derive_name_from_wire(*it.second);
+ new_cells[it.second->name] = it.second;
+ }
+ module->cells_.swap(new_cells);
+ }
+ }
+ else
if (flag_enumerate)
{
extra_args(args, argidx, design);
@@ -206,10 +346,12 @@ struct RenamePass : public Pass {
if (!design->selected_active_module.empty())
{
if (design->modules_.count(design->selected_active_module) > 0)
- rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name);
+ rename_in_module(design->modules_.at(design->selected_active_module), from_name, to_name, flag_output);
}
else
{
+ if (flag_output)
+ log_cmd_error("Mode -output requires that there is an active module selected.\n");
for (auto &mod : design->modules_) {
if (mod.first == from_name || RTLIL::unescape_id(mod.first) == from_name) {
to_name = RTLIL::escape_id(to_name);
diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc
index d97aa2b3..b5e8ef1a 100644
--- a/passes/cmds/select.cc
+++ b/passes/cmds/select.cc
@@ -896,6 +896,29 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
select_filter_active_mod(design, work_stack.back());
}
+static std::string describe_selection_for_assert(RTLIL::Design *design, RTLIL::Selection *sel)
+{
+ std::string desc = "Selection contains:\n";
+ for (auto mod_it : design->modules_)
+ {
+ if (sel->selected_module(mod_it.first)) {
+ for (auto &it : mod_it.second->wires_)
+ if (sel->selected_member(mod_it.first, it.first))
+ desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first));
+ for (auto &it : mod_it.second->memories)
+ if (sel->selected_member(mod_it.first, it.first))
+ desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first));
+ for (auto &it : mod_it.second->cells_)
+ if (sel->selected_member(mod_it.first, it.first))
+ desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first));
+ for (auto &it : mod_it.second->processes)
+ if (sel->selected_member(mod_it.first, it.first))
+ desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first));
+ }
+ }
+ return desc;
+}
+
PRIVATE_NAMESPACE_END
YOSYS_NAMESPACE_BEGIN
@@ -964,7 +987,7 @@ struct SelectPass : public Pass {
log("list of selected objects.\n");
log("\n");
log("Note that many commands support an optional [selection] argument that can be\n");
- log("used to YS_OVERRIDE the global selection for the command. The syntax of this\n");
+ log("used to override the global selection for the command. The syntax of this\n");
log("optional argument is identical to the syntax of the <selection> argument\n");
log("described here.\n");
log("\n");
@@ -1394,7 +1417,12 @@ struct SelectPass : public Pass {
log_cmd_error("No selection to check.\n");
work_stack.back().optimize(design);
if (!work_stack.back().empty())
- log_error("Assertion failed: selection is not empty:%s\n", sel_str.c_str());
+ {
+ RTLIL::Selection *sel = &work_stack.back();
+ sel->optimize(design);
+ std::string desc = describe_selection_for_assert(design, sel);
+ log_error("Assertion failed: selection is not empty:%s\n%s", sel_str.c_str(), desc.c_str());
+ }
return;
}
@@ -1404,7 +1432,12 @@ struct SelectPass : public Pass {
log_cmd_error("No selection to check.\n");
work_stack.back().optimize(design);
if (work_stack.back().empty())
- log_error("Assertion failed: selection is empty:%s\n", sel_str.c_str());
+ {
+ RTLIL::Selection *sel = &work_stack.back();
+ sel->optimize(design);
+ std::string desc = describe_selection_for_assert(design, sel);
+ log_error("Assertion failed: selection is empty:%s\n%s", sel_str.c_str(), desc.c_str());
+ }
return;
}
@@ -1431,14 +1464,23 @@ struct SelectPass : public Pass {
total_count++;
}
if (assert_count >= 0 && assert_count != total_count)
- log_error("Assertion failed: selection contains %d elements instead of the asserted %d:%s\n",
- total_count, assert_count, sel_str.c_str());
+ {
+ std::string desc = describe_selection_for_assert(design, sel);
+ log_error("Assertion failed: selection contains %d elements instead of the asserted %d:%s\n%s",
+ total_count, assert_count, sel_str.c_str(), desc.c_str());
+ }
if (assert_max >= 0 && assert_max < total_count)
- log_error("Assertion failed: selection contains %d elements, more than the maximum number %d:%s\n",
- total_count, assert_max, sel_str.c_str());
+ {
+ std::string desc = describe_selection_for_assert(design, sel);
+ log_error("Assertion failed: selection contains %d elements, more than the maximum number %d:%s\n%s",
+ total_count, assert_max, sel_str.c_str(), desc.c_str());
+ }
if (assert_min >= 0 && assert_min > total_count)
- log_error("Assertion failed: selection contains %d elements, less than the minimum number %d:%s\n",
- total_count, assert_min, sel_str.c_str());
+ {
+ std::string desc = describe_selection_for_assert(design, sel);
+ log_error("Assertion failed: selection contains %d elements, less than the minimum number %d:%s\n%s",
+ total_count, assert_min, sel_str.c_str(), desc.c_str());
+ }
return;
}
diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc
index a1dfa9b5..f6949c82 100644
--- a/passes/cmds/setundef.cc
+++ b/passes/cmds/setundef.cc
@@ -137,12 +137,15 @@ struct SetundefPass : public Pass {
log(" replace with $anyconst drivers (for formal)\n");
log("\n");
log(" -random <seed>\n");
- log(" replace with random bits using the specified integer als seed\n");
+ log(" replace with random bits using the specified integer as seed\n");
log(" value for the random number generator.\n");
log("\n");
log(" -init\n");
log(" also create/update init values for flip-flops\n");
log("\n");
+ log(" -params\n");
+ log(" replace undef in cell parameters\n");
+ log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
@@ -150,6 +153,7 @@ struct SetundefPass : public Pass {
bool undriven_mode = false;
bool expose_mode = false;
bool init_mode = false;
+ bool params_mode = false;
SetundefWorker worker;
log_header(design, "Executing SETUNDEF pass (replace undef values with defined constants).\n");
@@ -199,6 +203,10 @@ struct SetundefPass : public Pass {
init_mode = true;
continue;
}
+ if (args[argidx] == "-params") {
+ params_mode = true;
+ continue;
+ }
if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) {
got_value = true;
worker.next_bit_mode = MODE_RANDOM;
@@ -228,6 +236,18 @@ struct SetundefPass : public Pass {
for (auto module : design->selected_modules())
{
+ if (params_mode)
+ {
+ for (auto *cell : module->selected_cells()) {
+ for (auto &parameter : cell->parameters) {
+ for (auto &bit : parameter.second.bits) {
+ if (bit > RTLIL::State::S1)
+ bit = worker.next_bit();
+ }
+ }
+ }
+ }
+
if (undriven_mode)
{
if (!module->processes.empty())
diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc
index a4887324..58acd302 100644
--- a/passes/cmds/show.cc
+++ b/passes/cmds/show.cc
@@ -623,7 +623,7 @@ struct ShowPass : public Pass {
log(" assigned to each unique value of this attribute.\n");
log("\n");
log(" -width\n");
- log(" annotate busses with a label indicating the width of the bus.\n");
+ log(" annotate buses with a label indicating the width of the bus.\n");
log("\n");
log(" -signed\n");
log(" mark ports (A, B) that are declared as signed (using the [AB]_SIGNED\n");
diff --git a/passes/cmds/tee.cc b/passes/cmds/tee.cc
index ff80f385..ee96ace8 100644
--- a/passes/cmds/tee.cc
+++ b/passes/cmds/tee.cc
@@ -37,7 +37,7 @@ struct TeePass : public Pass {
log("specified logfile(s).\n");
log("\n");
log(" -q\n");
- log(" Do not print output to the normal destination (console and/or log file)\n");
+ log(" Do not print output to the normal destination (console and/or log file).\n");
log("\n");
log(" -o logfile\n");
log(" Write output to this file, truncate if exists.\n");
@@ -46,7 +46,7 @@ struct TeePass : public Pass {
log(" Write output to this file, append if exists.\n");
log("\n");
log(" +INT, -INT\n");
- log(" Add/subract INT from the -v setting for this command.\n");
+ log(" Add/subtract INT from the -v setting for this command.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
diff --git a/passes/equiv/Makefile.inc b/passes/equiv/Makefile.inc
index dd7b3be0..27ea54b2 100644
--- a/passes/equiv/Makefile.inc
+++ b/passes/equiv/Makefile.inc
@@ -9,4 +9,4 @@ OBJS += passes/equiv/equiv_induct.o
OBJS += passes/equiv/equiv_struct.o
OBJS += passes/equiv/equiv_purge.o
OBJS += passes/equiv/equiv_mark.o
-
+OBJS += passes/equiv/equiv_opt.o
diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc
index b1f88d55..dbd8682e 100644
--- a/passes/equiv/equiv_make.cc
+++ b/passes/equiv/equiv_make.cc
@@ -40,6 +40,16 @@ struct EquivMakeWorker
pool<SigBit> undriven_bits;
SigMap assign_map;
+ dict<SigBit, pool<Cell*>> bit2driven; // map: bit <--> and its driven cells
+
+ CellTypes comb_ct;
+
+ EquivMakeWorker()
+ {
+ comb_ct.setup_internals();
+ comb_ct.setup_stdcells();
+ }
+
void read_blacklists()
{
for (auto fn : blacklists)
@@ -278,16 +288,31 @@ struct EquivMakeWorker
}
}
+ init_bit2driven();
+
+ pool<Cell*> visited_cells;
for (auto c : cells_list)
for (auto &conn : c->connections())
if (!ct.cell_output(c->type, conn.first)) {
SigSpec old_sig = assign_map(conn.second);
SigSpec new_sig = rd_signal_map(old_sig);
- if (old_sig != new_sig) {
- log("Changing input %s of cell %s (%s): %s -> %s\n",
- log_id(conn.first), log_id(c), log_id(c->type),
- log_signal(old_sig), log_signal(new_sig));
- c->setPort(conn.first, new_sig);
+
+ if(old_sig != new_sig) {
+ SigSpec tmp_sig = old_sig;
+ for (int i = 0; i < GetSize(old_sig); i++) {
+ SigBit old_bit = old_sig[i], new_bit = new_sig[i];
+
+ visited_cells.clear();
+ if (check_signal_in_fanout(visited_cells, old_bit, new_bit))
+ continue;
+
+ log("Changing input %s of cell %s (%s): %s -> %s\n",
+ log_id(conn.first), log_id(c), log_id(c->type),
+ log_signal(old_bit), log_signal(new_bit));
+
+ tmp_sig[i] = new_bit;
+ }
+ c->setPort(conn.first, tmp_sig);
}
}
@@ -378,6 +403,57 @@ struct EquivMakeWorker
}
}
+ void init_bit2driven()
+ {
+ for (auto cell : equiv_mod->cells()) {
+ if (!ct.cell_known(cell->type) && !cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_", "$ff", "$_FF_"))
+ continue;
+ for (auto &conn : cell->connections())
+ {
+ if (yosys_celltypes.cell_input(cell->type, conn.first))
+ for (auto bit : assign_map(conn.second))
+ {
+ bit2driven[bit].insert(cell);
+ }
+ }
+ }
+ }
+
+ bool check_signal_in_fanout(pool<Cell*> & visited_cells, SigBit source_bit, SigBit target_bit)
+ {
+ if (source_bit == target_bit)
+ return true;
+
+ if (bit2driven.count(source_bit) == 0)
+ return false;
+
+ auto driven_cells = bit2driven.at(source_bit);
+ for (auto driven_cell: driven_cells)
+ {
+ bool is_comb = comb_ct.cell_known(driven_cell->type);
+ if (!is_comb)
+ continue;
+
+ if (visited_cells.count(driven_cell) > 0)
+ continue;
+ visited_cells.insert(driven_cell);
+
+ for (auto &conn: driven_cell->connections())
+ {
+ if (yosys_celltypes.cell_input(driven_cell->type, conn.first))
+ continue;
+
+ for (auto bit: conn.second) {
+ bool is_in_fanout = check_signal_in_fanout(visited_cells, bit, target_bit);
+ if (is_in_fanout == true)
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
void run()
{
copy_to_equiv();
diff --git a/passes/equiv/equiv_opt.cc b/passes/equiv/equiv_opt.cc
new file mode 100644
index 00000000..86550a69
--- /dev/null
+++ b/passes/equiv/equiv_opt.cc
@@ -0,0 +1,157 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2018 whitequark <whitequark@whitequark.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/register.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct EquivOptPass:public ScriptPass
+{
+ EquivOptPass() : ScriptPass("equiv_opt", "prove equivalence for optimized circuit") { }
+
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" equiv_opt [options] [command]\n");
+ log("\n");
+ log("This command checks circuit equivalence before and after an optimization pass.\n");
+ log("\n");
+ log(" -run <from_label>:<to_label>\n");
+ log(" only run the commands between the labels (see below). an empty\n");
+ log(" from label is synonymous to the start of the command list, and empty to\n");
+ log(" label is synonymous to the end of the command list.\n");
+ log("\n");
+ log(" -map <filename>\n");
+ log(" expand the modules in this file before proving equivalence. this is\n");
+ 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("\n");
+ log("The following commands are executed by this verification command:\n");
+ help_script();
+ log("\n");
+ }
+
+ std::string command, techmap_opts;
+ bool assert;
+
+ void clear_flags() YS_OVERRIDE
+ {
+ command = "";
+ techmap_opts = "";
+ assert = false;
+ }
+
+ void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE
+ {
+ string run_from, run_to;
+ clear_flags();
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-run" && argidx + 1 < args.size()) {
+ size_t pos = args[argidx + 1].find(':');
+ if (pos == std::string::npos)
+ break;
+ run_from = args[++argidx].substr(0, pos);
+ run_to = args[argidx].substr(pos + 1);
+ continue;
+ }
+ if (args[argidx] == "-map" && argidx + 1 < args.size()) {
+ techmap_opts += " -map " + args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-assert") {
+ assert = true;
+ continue;
+ }
+ break;
+ }
+
+ for (; argidx < args.size(); argidx++) {
+ if (command.empty()) {
+ if (args[argidx].substr(0, 1) == "-")
+ cmd_error(args, argidx, "Unknown option.");
+ } else {
+ command += " ";
+ }
+ command += args[argidx];
+ }
+
+ if (command.empty())
+ log_cmd_error("No optimization pass specified!\n");
+
+ if (!design->full_selection())
+ log_cmd_error("This command only operates on fully selected designs!\n");
+
+ log_header(design, "Executing EQUIV_OPT pass.\n");
+ log_push();
+
+ run_script(design, run_from, run_to);
+
+ log_pop();
+ }
+
+ void script() YS_OVERRIDE
+ {
+ if (check_label("run_pass")) {
+ run("hierarchy -auto-top");
+ run("design -save preopt");
+ if (help_mode)
+ run("[command]");
+ else
+ run(command);
+ run("design -stash postopt");
+ }
+
+ if (check_label("prepare")) {
+ run("design -copy-from preopt -as gold A:top");
+ run("design -copy-from postopt -as gate A:top");
+ }
+
+ if ((!techmap_opts.empty() || help_mode) && check_label("techmap", "(only with -map)")) {
+ string opts;
+ if (help_mode)
+ opts = " -map <filename> ...";
+ else
+ opts = techmap_opts;
+ run("techmap -D EQUIV -autoproc" + opts);
+ }
+
+ if (check_label("prove")) {
+ run("equiv_make gold gate equiv");
+ run("equiv_induct equiv");
+ if (help_mode)
+ run("equiv_status [-assert] equiv");
+ else if (assert)
+ run("equiv_status -assert equiv");
+ else
+ run("equiv_status equiv");
+ }
+
+ if (check_label("restore")) {
+ run("design -load preopt");
+ }
+ }
+} EquivOptPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc
index fc504e98..5ae991b2 100644
--- a/passes/fsm/fsm_detect.cc
+++ b/passes/fsm/fsm_detect.cc
@@ -196,13 +196,13 @@ static void detect_fsm(RTLIL::Wire *wire)
vector<string> warnings;
if (is_module_port)
- warnings.push_back("Forcing fsm recoding on module port might result in larger circuit.\n");
+ warnings.push_back("Forcing FSM recoding on module port might result in larger circuit.\n");
if (!looks_like_good_state_reg)
- warnings.push_back("Users of state reg look like fsm recoding might result in larger circuit.\n");
+ warnings.push_back("Users of state reg look like FSM recoding might result in larger circuit.\n");
if (has_init_attr)
- warnings.push_back("Init value on fsm state registers are ignored. Possible simulation-synthesis mismatch!");
+ warnings.push_back("Initialization value on FSM state register is ignored. Possible simulation-synthesis mismatch!\n");
if (!looks_like_state_reg)
warnings.push_back("Doesn't look like a proper FSM. Possible simulation-synthesis mismatch!\n");
@@ -236,7 +236,7 @@ static void detect_fsm(RTLIL::Wire *wire)
log(" Users of register don't seem to benefit from recoding.\n");
if (has_init_attr)
- log(" Register has an initialization value.");
+ log(" Register has an initialization value.\n");
if (is_self_resetting)
log(" Circuit seems to be self-resetting.\n");
diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc
index 67551f67..6095eaf3 100644
--- a/passes/fsm/fsm_extract.cc
+++ b/passes/fsm/fsm_extract.cc
@@ -178,7 +178,7 @@ undef_bit_in_next_state:
log_state_in = fsm_data.state_table.at(state_in);
if (states.count(ce.values_map(ce.assign_map(dff_in)).as_const()) == 0) {
- log(" transition: %10s %s -> INVALID_STATE(%s) %s <ignored invalid transistion!>%s\n",
+ log(" transition: %10s %s -> INVALID_STATE(%s) %s <ignored invalid transition!>%s\n",
log_signal(log_state_in), log_signal(tr.ctrl_in),
log_signal(ce.values_map(ce.assign_map(dff_in))), log_signal(tr.ctrl_out),
undef_bit_in_next_state_mode ? " SHORTENED" : "");
@@ -194,7 +194,7 @@ undef_bit_in_next_state:
log_signal(log_state_in), log_signal(tr.ctrl_in),
log_signal(fsm_data.state_table[tr.state_out]), log_signal(tr.ctrl_out));
} else {
- log(" transition: %10s %s -> %10s %s <ignored undef transistion!>\n",
+ log(" transition: %10s %s -> %10s %s <ignored undef transition!>\n",
log_signal(log_state_in), log_signal(tr.ctrl_in),
log_signal(fsm_data.state_table[tr.state_out]), log_signal(tr.ctrl_out));
}
diff --git a/passes/fsm/fsm_opt.cc b/passes/fsm/fsm_opt.cc
index 3a6ac274..048daee5 100644
--- a/passes/fsm/fsm_opt.cc
+++ b/passes/fsm/fsm_opt.cc
@@ -72,7 +72,8 @@ struct FsmOpt
new_transition_table.swap(fsm_data.transition_table);
new_state_table.swap(fsm_data.state_table);
- fsm_data.reset_state = old_to_new_state.at(fsm_data.reset_state);
+ if (fsm_data.reset_state != -1)
+ fsm_data.reset_state = old_to_new_state.at(fsm_data.reset_state);
}
}
diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc
index 5df69848..88c339e8 100644
--- a/passes/hierarchy/hierarchy.cc
+++ b/passes/hierarchy/hierarchy.cc
@@ -2,6 +2,7 @@
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2018 Ruben Undheim <ruben.undheim@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -139,25 +140,73 @@ void generate(RTLIL::Design *design, const std::vector<std::string> &celltypes,
}
}
+// Return the "basic" type for an array item.
+std::string basic_cell_type(const std::string celltype, int pos[3] = nullptr) {
+ std::string basicType = celltype;
+ if (celltype.substr(0, 7) == "$array:") {
+ int pos_idx = celltype.find_first_of(':');
+ int pos_num = celltype.find_first_of(':', pos_idx + 1);
+ int pos_type = celltype.find_first_of(':', pos_num + 1);
+ basicType = celltype.substr(pos_type + 1);
+ if (pos != nullptr) {
+ pos[0] = pos_idx;
+ pos[1] = pos_num;
+ pos[2] = pos_type;
+ }
+ }
+ return basicType;
+}
+
bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector<std::string> &libdirs)
{
bool did_something = false;
std::map<RTLIL::Cell*, std::pair<int, int>> array_cells;
std::string filename;
+ bool has_interface_ports = false;
+
+ // If any of the ports are actually interface ports, we will always need to
+ // reprocess the module:
+ if(!module->get_bool_attribute("\\interfaces_replaced_in_module")) {
+ for (auto &wire : module->wires_) {
+ if ((wire.second->port_input || wire.second->port_output) && wire.second->get_bool_attribute("\\is_interface"))
+ has_interface_ports = true;
+ }
+ }
+
+ // Always keep track of all derived interfaces available in the current module in 'interfaces_in_module':
+ dict<RTLIL::IdString, RTLIL::Module*> interfaces_in_module;
+ for (auto &cell_it : module->cells_)
+ {
+ RTLIL::Cell *cell = cell_it.second;
+ if(cell->get_bool_attribute("\\is_interface")) {
+ RTLIL::Module *intf_module = design->modules_[cell->type];
+ interfaces_in_module[cell->name] = intf_module;
+ }
+ }
+
for (auto &cell_it : module->cells_)
{
RTLIL::Cell *cell = cell_it.second;
+ bool has_interfaces_not_found = false;
+
+ std::vector<RTLIL::IdString> connections_to_remove;
+ std::vector<RTLIL::IdString> connections_to_add_name;
+ std::vector<RTLIL::SigSpec> connections_to_add_signal;
if (cell->type.substr(0, 7) == "$array:") {
- int pos_idx = cell->type.str().find_first_of(':');
- int pos_num = cell->type.str().find_first_of(':', pos_idx + 1);
- int pos_type = cell->type.str().find_first_of(':', pos_num + 1);
+ int pos[3];
+ basic_cell_type(cell->type.str(), pos);
+ int pos_idx = pos[0];
+ int pos_num = pos[1];
+ int pos_type = pos[2];
int idx = atoi(cell->type.str().substr(pos_idx + 1, pos_num).c_str());
int num = atoi(cell->type.str().substr(pos_num + 1, pos_type).c_str());
array_cells[cell] = std::pair<int, int>(idx, num);
cell->type = cell->type.str().substr(pos_type + 1);
}
+ dict<RTLIL::IdString, RTLIL::Module*> interfaces_to_add_to_submodule;
+ dict<RTLIL::IdString, RTLIL::IdString> modports_used_in_submodule;
if (design->modules_.count(cell->type) == 0)
{
@@ -200,11 +249,85 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
if (design->modules_.count(cell->type) == 0)
log_error("File `%s' from libdir does not declare module `%s'.\n", filename.c_str(), cell->type.c_str());
did_something = true;
- } else
+ } else {
+
+ RTLIL::Module *mod = design->module(cell->type);
+
+ // Go over all connections and see if any of them are SV interfaces. If they are, then add the replacements to
+ // some lists, so that the ports for sub-modules can be replaced further down:
+ for (auto &conn : cell->connections()) {
+ if(mod->wires_.count(conn.first) != 0 && mod->wire(conn.first)->get_bool_attribute("\\is_interface")) { // Check if the connection is present as an interface in the sub-module's port list
+ //const pool<string> &interface_type_pool = mod->wire(conn.first)->get_strpool_attribute("\\interface_type");
+ //for (auto &d : interface_type_pool) { // TODO: Compare interface type to type in parent module (not crucially important, but good for robustness)
+ //}
+
+ // Find if the sub-module has set a modport for the current interface connection:
+ const pool<string> &interface_modport_pool = mod->wire(conn.first)->get_strpool_attribute("\\interface_modport");
+ std::string interface_modport = "";
+ for (auto &d : interface_modport_pool) {
+ interface_modport = "\\" + d;
+ }
+ if(conn.second.bits().size() == 1 && conn.second.bits()[0].wire->get_bool_attribute("\\is_interface")) { // Check if the connected wire is a potential interface in the parent module
+ std::string interface_name_str = conn.second.bits()[0].wire->name.str();
+ interface_name_str.replace(0,23,""); // Strip the prefix '$dummywireforinterface' from the dummy wire to get the name
+ interface_name_str = "\\" + interface_name_str;
+ RTLIL::IdString interface_name = interface_name_str;
+ bool not_found_interface = false;
+ if(module->get_bool_attribute("\\interfaces_replaced_in_module")) { // If 'interfaces' in the cell have not be been handled yet, there is no need to derive the sub-module either
+ // Check if the interface instance is present in module:
+ // Interface instances may either have the plain name or the name appended with '_inst_from_top_dummy'.
+ // Check for both of them here
+ int nexactmatch = interfaces_in_module.count(interface_name) > 0;
+ std::string interface_name_str2 = interface_name_str + "_inst_from_top_dummy";
+ RTLIL::IdString interface_name2 = interface_name_str2;
+ int nmatch2 = interfaces_in_module.count(interface_name2) > 0;
+ if (nexactmatch > 0 || nmatch2 > 0) {
+ if (nexactmatch != 0) // Choose the one with the plain name if it exists
+ interface_name2 = interface_name;
+ RTLIL::Module *mod_replace_ports = interfaces_in_module.at(interface_name2);
+ for (auto &mod_wire : mod_replace_ports->wires_) { // Go over all wires in interface, and add replacements to lists.
+ std::string signal_name1 = conn.first.str() + "." + log_id(mod_wire.first);
+ std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire.first);
+ connections_to_add_name.push_back(RTLIL::IdString(signal_name1));
+ if(module->wires_.count(signal_name2) == 0) {
+ log_error("Could not find signal '%s' in '%s'\n", signal_name2.c_str(), log_id(module->name));
+ }
+ else {
+ RTLIL::Wire *wire_in_parent = module->wire(signal_name2);
+ connections_to_add_signal.push_back(wire_in_parent);
+ }
+ }
+ connections_to_remove.push_back(conn.first);
+ interfaces_to_add_to_submodule[conn.first] = interfaces_in_module.at(interface_name2);
+
+ // Add modports to a dict which will be passed to AstModule::derive
+ if (interface_modport != "") {
+ modports_used_in_submodule[conn.first] = interface_modport;
+ }
+ }
+ else not_found_interface = true;
+ }
+ else not_found_interface = true;
+ // If the interface instance has not already been derived in the module, we cannot complete at this stage. Set "has_interfaces_not_found"
+ // which will delay the expansion of this cell:
+ if (not_found_interface) {
+ // If we have already gone over all cells in this module, and the interface has still not been found - flag it as an error:
+ if(!(module->get_bool_attribute("\\cells_not_processed"))) {
+ log_warning("Could not find interface instance for `%s' in `%s'\n", log_id(interface_name), log_id(module));
+ }
+ else {
+ // Only set has_interfaces_not_found if it would be possible to find them, since otherwiser we will end up in an infinite loop:
+ has_interfaces_not_found = true;
+ }
+ }
+ }
+ }
+ }
+ //
+
if (flag_check || flag_simcheck)
{
- RTLIL::Module *mod = design->module(cell->type);
- for (auto &conn : cell->connections())
+ for (auto &conn : cell->connections()) {
if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') {
int id = atoi(conn.first.c_str()+1);
if (id <= 0 || id > GetSize(mod->ports))
@@ -213,11 +336,15 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
} else if (mod->wire(conn.first) == nullptr || mod->wire(conn.first)->port_id == 0)
log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a port named '%s'.\n",
log_id(cell->type), log_id(module), log_id(cell), log_id(conn.first));
+ }
for (auto &param : cell->parameters)
if (mod->avail_parameters.count(param.first) == 0 && param.first[0] != '$' && strchr(param.first.c_str(), '.') == NULL)
log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a parameter named '%s'.\n",
log_id(cell->type), log_id(module), log_id(cell), log_id(param.first));
+
+ }
}
+ RTLIL::Module *mod = design->modules_[cell->type];
if (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) {
if (flag_simcheck)
@@ -226,15 +353,62 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
continue;
}
- if (cell->parameters.size() == 0)
+ // If interface instances not yet found, skip cell for now, and say we did something, so that we will return back here:
+ if(has_interfaces_not_found) {
+ did_something = true; // waiting for interfaces to be handled
continue;
+ }
- RTLIL::Module *mod = design->modules_[cell->type];
- cell->type = mod->derive(design, cell->parameters);
+ // Do the actual replacements of the SV interface port connection with the individual signal connections:
+ for(unsigned int i=0;i<connections_to_add_name.size();i++) {
+ cell->connections_[connections_to_add_name[i]] = connections_to_add_signal[i];
+ }
+ // Remove the connection for the interface itself:
+ for(unsigned int i=0;i<connections_to_remove.size();i++) {
+ cell->connections_.erase(connections_to_remove[i]);
+ }
+
+ // If there are no overridden parameters AND not interfaces, then we can use the existing module instance as the type
+ // for the cell:
+ if (cell->parameters.size() == 0 && (interfaces_to_add_to_submodule.size() == 0 || !(cell->get_bool_attribute("\\module_not_derived")))) {
+ // If the cell being processed is an the interface instance itself, go down to "handle_interface_instance:",
+ // so that the signals of the interface are added to the parent module.
+ if (mod->get_bool_attribute("\\is_interface")) {
+ goto handle_interface_instance;
+ }
+ continue;
+ }
+
+ cell->type = mod->derive(design, cell->parameters, interfaces_to_add_to_submodule, modports_used_in_submodule);
cell->parameters.clear();
did_something = true;
+
+ handle_interface_instance:
+
+ // We add all the signals of the interface explicitly to the parent module. This is always needed when we encounter
+ // an interface instance:
+ if (mod->get_bool_attribute("\\is_interface") && cell->get_bool_attribute("\\module_not_derived")) {
+ cell->set_bool_attribute("\\is_interface");
+ RTLIL::Module *derived_module = design->modules_[cell->type];
+ interfaces_in_module[cell->name] = derived_module;
+ did_something = true;
+ }
+ // We clear 'module_not_derived' such that we will not rederive the cell again (needed when there are interfaces connected to the cell)
+ cell->attributes.erase("\\module_not_derived");
+ }
+ // Clear the attribute 'cells_not_processed' such that it can be known that we
+ // have been through all cells at least once, and that we can know whether
+ // to flag an error because of interface instances not found:
+ module->attributes.erase("\\cells_not_processed");
+
+
+ // If any interface instances or interface ports were found in the module, we need to rederive it completely:
+ if ((interfaces_in_module.size() > 0 || has_interface_ports) && !module->get_bool_attribute("\\interfaces_replaced_in_module")) {
+ module->reprocess_module(design, interfaces_in_module);
+ return did_something;
}
+
for (auto &it : array_cells)
{
RTLIL::Cell *cell = it.first;
@@ -284,10 +458,7 @@ void hierarchy_worker(RTLIL::Design *design, std::set<RTLIL::Module*, IdString::
for (auto cell : mod->cells()) {
std::string celltype = cell->type.str();
if (celltype.substr(0, 7) == "$array:") {
- int pos_idx = celltype.find_first_of(':');
- int pos_num = celltype.find_first_of(':', pos_idx + 1);
- int pos_type = celltype.find_first_of(':', pos_num + 1);
- celltype = celltype.substr(pos_type + 1);
+ celltype = basic_cell_type(celltype);
}
if (design->module(celltype))
hierarchy_worker(design, used, design->module(celltype), indent+4);
@@ -303,6 +474,20 @@ void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib)
for (auto &it : design->modules_)
if (used.count(it.second) == 0)
del_modules.push_back(it.second);
+ else {
+ // Now all interface ports must have been exploded, and it is hence
+ // safe to delete all of the remaining dummy interface ports:
+ pool<RTLIL::Wire*> del_wires;
+ for(auto &wire : it.second->wires_) {
+ if ((wire.second->port_input || wire.second->port_output) && wire.second->get_bool_attribute("\\is_interface")) {
+ del_wires.insert(wire.second);
+ }
+ }
+ if (del_wires.size() > 0) {
+ it.second->remove(del_wires);
+ it.second->fixup_ports();
+ }
+ }
int del_counter = 0;
for (auto mod : del_modules) {
@@ -333,14 +518,38 @@ int find_top_mod_score(Design *design, Module *module, dict<Module*, int> &db)
if (db.count(module) == 0) {
int score = 0;
db[module] = 0;
- for (auto cell : module->cells())
- if (design->module(cell->type))
- score = max(score, find_top_mod_score(design, design->module(cell->type), db) + 1);
+ for (auto cell : module->cells()) {
+ std::string celltype = cell->type.str();
+ // Is this an array instance
+ if (celltype.substr(0, 7) == "$array:") {
+ celltype = basic_cell_type(celltype);
+ }
+ // Is this cell a module instance?
+ auto instModule = design->module(celltype);
+ // If there is no instance for this, issue a warning.
+ if (instModule != nullptr) {
+ score = max(score, find_top_mod_score(design, instModule, db) + 1);
+ }
+ }
db[module] = score;
}
return db.at(module);
}
+RTLIL::Module *check_if_top_has_changed(Design *design, Module *top_mod)
+{
+ if(top_mod != NULL && top_mod->get_bool_attribute("\\initial_top"))
+ return top_mod;
+ else {
+ for (auto mod : design->modules()) {
+ if (mod->get_bool_attribute("\\top")) {
+ return mod;
+ }
+ }
+ }
+ return NULL;
+}
+
struct HierarchyPass : public Pass {
HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { }
void help() YS_OVERRIDE
@@ -360,7 +569,7 @@ struct HierarchyPass : public Pass {
log(" an unknown module is used as cell type.\n");
log("\n");
log(" -simcheck\n");
- log(" like -check, but also thow an error if blackbox modules are\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("\n");
log(" -purge_lib\n");
@@ -568,6 +777,14 @@ struct HierarchyPass : public Pass {
if (flag_simcheck && top_mod == nullptr)
log_error("Design has no top module.\n");
+ if (top_mod != NULL) {
+ for (auto &mod_it : design->modules_)
+ if (mod_it.second == top_mod)
+ mod_it.second->attributes["\\initial_top"] = RTLIL::Const(1);
+ else
+ mod_it.second->attributes.erase("\\initial_top");
+ }
+
bool did_something = true;
while (did_something)
{
@@ -586,19 +803,43 @@ struct HierarchyPass : public Pass {
if (expand_module(design, module, flag_check, flag_simcheck, libdirs))
did_something = true;
}
+
+
+ // The top module might have changed if interface instances have been detected in it:
+ RTLIL::Module *tmp_top_mod = check_if_top_has_changed(design, top_mod);
+ if (tmp_top_mod != NULL) {
+ if (tmp_top_mod != top_mod){
+ top_mod = tmp_top_mod;
+ did_something = true;
+ }
+ }
+
+ // Delete modules marked as 'to_delete':
+ std::vector<RTLIL::Module *> modules_to_delete;
+ for(auto &mod_it : design->modules_) {
+ if (mod_it.second->get_bool_attribute("\\to_delete")) {
+ modules_to_delete.push_back(mod_it.second);
+ }
+ }
+ for(size_t i=0; i<modules_to_delete.size(); i++) {
+ design->remove(modules_to_delete[i]);
+ }
}
+
if (top_mod != NULL) {
log_header(design, "Analyzing design hierarchy..\n");
hierarchy_clean(design, top_mod, purge_lib);
}
if (top_mod != NULL) {
- for (auto &mod_it : design->modules_)
+ for (auto &mod_it : design->modules_) {
if (mod_it.second == top_mod)
mod_it.second->attributes["\\top"] = RTLIL::Const(1);
else
mod_it.second->attributes.erase("\\top");
+ mod_it.second->attributes.erase("\\initial_top");
+ }
}
if (!nokeep_asserts) {
@@ -669,7 +910,7 @@ struct HierarchyPass : public Pass {
if (m == nullptr)
continue;
- if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty()) {
+ 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())
continue;
diff --git a/passes/hierarchy/uniquify.cc b/passes/hierarchy/uniquify.cc
index c88ecd82..e6154e94 100644
--- a/passes/hierarchy/uniquify.cc
+++ b/passes/hierarchy/uniquify.cc
@@ -87,6 +87,8 @@ struct UniquifyPass : public Pass {
smod->name = newname;
cell->type = newname;
smod->set_bool_attribute("\\unique");
+ if (smod->attributes.count("\\hdlname") == 0)
+ smod->attributes["\\hdlname"] = string(log_id(tmod->name));
design->add(smod);
did_something = true;
diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc
index e8552bbc..85ed1c05 100644
--- a/passes/memory/memory_bram.cc
+++ b/passes/memory/memory_bram.cc
@@ -472,8 +472,12 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
std::vector<SigSpec> new_wr_en(GetSize(old_wr_en));
std::vector<SigSpec> new_wr_data(GetSize(old_wr_data));
std::vector<SigSpec> new_rd_data(GetSize(old_rd_data));
+ std::vector<std::vector<State>> new_initdata;
std::vector<int> shuffle_map;
+ if (cell_init)
+ new_initdata.resize(mem_size);
+
for (auto &it : en_order)
{
auto &bits = bits_wr_en.at(it);
@@ -489,6 +493,10 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
}
for (int j = 0; j < rd_ports; j++)
new_rd_data[j].append(old_rd_data[j][bits[i]]);
+ if (cell_init) {
+ for (int j = 0; j < mem_size; j++)
+ new_initdata[j].push_back(initdata[j][bits[i]]);
+ }
shuffle_map.push_back(bits[i]);
}
@@ -499,6 +507,10 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
}
for (int j = 0; j < rd_ports; j++)
new_rd_data[j].append(State::Sx);
+ if (cell_init) {
+ for (int j = 0; j < mem_size; j++)
+ new_initdata[j].push_back(State::Sx);
+ }
shuffle_map.push_back(-1);
}
}
@@ -522,10 +534,15 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
for (int i = 0; i < rd_ports; i++)
rd_data.replace(i*mem_width, new_rd_data[i]);
+
+ if (cell_init) {
+ for (int i = 0; i < mem_size; i++)
+ initdata[i] = Const(new_initdata[i]);
+ }
}
// assign write ports
-
+ pair<SigBit, bool> wr_clkdom;
for (int cell_port_i = 0, bram_port_i = 0; cell_port_i < wr_ports; cell_port_i++)
{
bool clken = wr_clken[cell_port_i] == State::S1;
@@ -535,7 +552,7 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram,
pair<SigBit, bool> clkdom(clksig, clkpol);
if (!clken)
clkdom = pair<SigBit, bool>(State::S1, false);
-
+ wr_clkdom = clkdom;
log(" Write port #%d is in clock domain %s%s.\n",
cell_port_i, clkdom.second ? "" : "!",
clken ? log_signal(clkdom.first) : "~async~");
@@ -623,6 +640,8 @@ grow_read_ports:;
pi.sig_addr = SigSpec();
pi.sig_data = SigSpec();
pi.sig_en = SigSpec();
+ pi.make_outreg = false;
+ pi.make_transp = false;
}
new_portinfos.push_back(pi);
if (pi.dupidx == dup_count-1) {
@@ -700,7 +719,13 @@ grow_read_ports:;
if (read_transp.count(pi.transp) && read_transp.at(pi.transp) != transp) {
if (match.make_transp && wr_ports <= 1) {
pi.make_transp = true;
- enable_make_transp = true;
+ if (pi.clocks != 0) {
+ if (wr_ports == 1 && wr_clkdom != clkdom) {
+ log(" Bram port %c%d.%d cannot have soft transparency logic added as read and write clock domains differ.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ goto skip_bram_rport;
+ }
+ enable_make_transp = true;
+ }
} else {
log(" Bram port %c%d.%d has incompatible read transparency.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
goto skip_bram_rport;
@@ -895,17 +920,18 @@ grow_read_ports:;
} else {
SigSpec bram_dout = module->addWire(NEW_ID, bram.dbits);
c->setPort(stringf("\\%sDATA", pf), bram_dout);
-
- if (pi.make_outreg) {
+ if (pi.make_outreg && pi.make_transp) {
+ log(" Moving output register to address for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
+ SigSpec sig_addr_q = module->addWire(NEW_ID, bram.abits);
+ module->addDff(NEW_ID, pi.sig_clock, sig_addr, sig_addr_q, pi.effective_clkpol);
+ c->setPort(stringf("\\%sADDR", pf), sig_addr_q);
+ } else if (pi.make_outreg) {
SigSpec bram_dout_q = module->addWire(NEW_ID, bram.dbits);
if (!pi.sig_en.empty())
bram_dout = module->Mux(NEW_ID, bram_dout_q, bram_dout, pi.sig_en);
module->addDff(NEW_ID, pi.sig_clock, bram_dout, bram_dout_q, pi.effective_clkpol);
bram_dout = bram_dout_q;
- }
-
- if (pi.make_transp)
- {
+ } else if (pi.make_transp) {
log(" Adding extra logic for transparent port %c%d.%d.\n", pi.group + 'A', pi.index + 1, pi.dupidx + 1);
SigSpec transp_en_d = module->Mux(NEW_ID, SigSpec(0, make_transp_enbits),
diff --git a/passes/memory/memory_collect.cc b/passes/memory/memory_collect.cc
index 70d98713..369fcc84 100644
--- a/passes/memory/memory_collect.cc
+++ b/passes/memory/memory_collect.cc
@@ -184,9 +184,6 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory)
mem->parameters["\\OFFSET"] = Const(memory->start_offset);
mem->parameters["\\SIZE"] = Const(memory->size);
mem->parameters["\\ABITS"] = Const(addr_bits);
-
- while (GetSize(init_data) > 1 && init_data.bits.back() == State::Sx && init_data.bits[GetSize(init_data)-2] == State::Sx)
- init_data.bits.pop_back();
mem->parameters["\\INIT"] = init_data;
log_assert(sig_wr_clk.size() == wr_ports);
diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc
index 32df1917..220d2929 100644
--- a/passes/memory/memory_dff.cc
+++ b/passes/memory/memory_dff.cc
@@ -41,7 +41,7 @@ struct MemoryDffWorker
if (wire->attributes.count("\\init") == 0)
continue;
SigSpec sig = sigmap(wire);
- Const initval = wire->attributes.count("\\init");
+ Const initval = wire->attributes.at("\\init");
for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++)
if (initval[i] == State::S0 || initval[i] == State::S1)
init_bits.insert(sig[i]);
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc
index 0d01e9d3..c3e0a2a4 100644
--- a/passes/opt/Makefile.inc
+++ b/passes/opt/Makefile.inc
@@ -12,5 +12,6 @@ OBJS += passes/opt/share.o
OBJS += passes/opt/wreduce.o
OBJS += passes/opt/opt_demorgan.o
OBJS += passes/opt/rmports.o
+OBJS += passes/opt/opt_lut.o
endif
diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc
index 0ba233c6..a05db2a4 100644
--- a/passes/opt/opt_expr.cc
+++ b/passes/opt/opt_expr.cc
@@ -155,6 +155,13 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
new_b.append_bit(it.first.second);
}
+ 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));
+ module->connect(new_y, new_b);
+ module->connect(new_conn);
+ continue;
+ }
+
RTLIL::Cell *c = module->addCell(NEW_ID, cell->type);
c->setPort("\\A", new_a);
@@ -259,6 +266,22 @@ bool is_one_or_minus_one(const Const &value, bool is_signed, bool &is_negative)
return last_bit_one;
}
+int get_highest_hot_index(RTLIL::SigSpec signal)
+{
+ for (int i = GetSize(signal) - 1; i >= 0; i--)
+ {
+ if (signal[i] == RTLIL::State::S0)
+ continue;
+
+ if (signal[i] == RTLIL::State::S1)
+ return i;
+
+ break;
+ }
+
+ return -1;
+}
+
// if the signal has only one bit set, return the index of that bit.
// otherwise return -1
int get_onehot_bit_index(RTLIL::SigSpec signal)
@@ -1344,118 +1367,139 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
}
- // replace a<0 or a>=0 with the top bit of a
+ // simplify comparisons
if (do_fine && (cell->type == "$lt" || cell->type == "$ge" || cell->type == "$gt" || cell->type == "$le"))
{
- //used to decide whether the signal needs to be negated
- bool is_lt = false;
-
- //references the variable signal in the comparison
- RTLIL::SigSpec sigVar;
-
- //references the constant signal in the comparison
- RTLIL::SigSpec sigConst;
-
- // note that this signal must be constant for the optimization
- // to take place, but it is not checked beforehand.
- // If new passes are added, this signal must be checked for const-ness
-
- //width of the variable port
- int width;
- int const_width;
-
- bool var_signed;
-
- if (cell->type == "$lt" || cell->type == "$ge") {
- is_lt = cell->type == "$lt" ? 1 : 0;
- sigVar = cell->getPort("\\A");
- sigConst = cell->getPort("\\B");
- width = cell->parameters["\\A_WIDTH"].as_int();
- const_width = cell->parameters["\\B_WIDTH"].as_int();
- var_signed = cell->parameters["\\A_SIGNED"].as_bool();
- } else
- if (cell->type == "$gt" || cell->type == "$le") {
- is_lt = cell->type == "$gt" ? 1 : 0;
- sigVar = cell->getPort("\\B");
- sigConst = cell->getPort("\\A");
- width = cell->parameters["\\B_WIDTH"].as_int();
- const_width = cell->parameters["\\A_WIDTH"].as_int();
- var_signed = cell->parameters["\\B_SIGNED"].as_bool();
- } else
- log_abort();
+ IdString cmp_type = cell->type;
+ SigSpec var_sig = cell->getPort("\\A");
+ SigSpec const_sig = cell->getPort("\\B");
+ int var_width = cell->parameters["\\A_WIDTH"].as_int();
+ int const_width = cell->parameters["\\B_WIDTH"].as_int();
+ bool is_signed = cell->getParam("\\A_SIGNED").as_bool();
- // replace a(signed) < 0 with the high bit of a
- if (sigConst.is_fully_const() && sigConst.is_fully_zero() && var_signed == true)
+ if (!const_sig.is_fully_const())
{
- RTLIL::SigSpec a_prime(RTLIL::State::S0, cell->parameters["\\Y_WIDTH"].as_int());
- a_prime[0] = sigVar[width - 1];
- if (is_lt) {
- log("Replacing %s cell `%s' (implementing X<0) with X[%d]: %s\n",
- log_id(cell->type), log_id(cell), width-1, log_signal(a_prime));
- module->connect(cell->getPort("\\Y"), a_prime);
- module->remove(cell);
- } else {
- log("Replacing %s cell `%s' (implementing X>=0) with ~X[%d]: %s\n",
- log_id(cell->type), log_id(cell), width-1, log_signal(a_prime));
- module->addNot(NEW_ID, a_prime, cell->getPort("\\Y"));
- module->remove(cell);
- }
- did_something = true;
- goto next_cell;
- } else
- if (sigConst.is_fully_const() && sigConst.is_fully_def() && var_signed == false)
+ std::swap(var_sig, const_sig);
+ std::swap(var_width, const_width);
+ if (cmp_type == "$gt")
+ cmp_type = "$lt";
+ else if (cmp_type == "$lt")
+ cmp_type = "$gt";
+ else if (cmp_type == "$ge")
+ cmp_type = "$le";
+ else if (cmp_type == "$le")
+ cmp_type = "$ge";
+ }
+
+ if (const_sig.is_fully_def() && const_sig.is_fully_const())
{
- if (sigConst.is_fully_zero()) {
- RTLIL::SigSpec a_prime(RTLIL::State::S0, 1);
- if (is_lt) {
- log("Replacing %s cell `%s' (implementing unsigned X<0) with constant false.\n",
- log_id(cell->type), log_id(cell));
- a_prime[0] = RTLIL::State::S0;
- } else {
- log("Replacing %s cell `%s' (implementing unsigned X>=0) with constant true.\n",
- log_id(cell->type), log_id(cell));
- a_prime[0] = RTLIL::State::S1;
+ std::string condition, replacement;
+ SigSpec replace_sig(State::S0, GetSize(cell->getPort("\\Y")));
+ bool replace = false;
+ bool remove = false;
+
+ if (!is_signed)
+ { /* unsigned */
+ if (const_sig.is_fully_zero() && cmp_type == "$lt") {
+ condition = "unsigned X<0";
+ replacement = "constant 0";
+ replace_sig[0] = State::S0;
+ replace = true;
+ }
+ if (const_sig.is_fully_zero() && cmp_type == "$ge") {
+ condition = "unsigned X>=0";
+ replacement = "constant 1";
+ replace_sig[0] = State::S1;
+ replace = true;
+ }
+ if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$gt") {
+ condition = "unsigned X>~0";
+ replacement = "constant 0";
+ replace_sig[0] = State::S0;
+ replace = true;
+ }
+ if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$le") {
+ condition = "unsigned X<=~0";
+ replacement = "constant 1";
+ replace_sig[0] = State::S1;
+ replace = true;
}
- module->connect(cell->getPort("\\Y"), a_prime);
- module->remove(cell);
- did_something = true;
- goto next_cell;
- }
- int const_bit_set = get_onehot_bit_index(sigConst);
- if (const_bit_set >= 0 && const_bit_set < width) {
- int bit_set = const_bit_set;
- RTLIL::SigSpec a_prime(RTLIL::State::S0, width - bit_set);
- for (int i = bit_set; i < width; i++) {
- a_prime[i - bit_set] = sigVar[i];
+ int const_bit_hot = get_onehot_bit_index(const_sig);
+ if (const_bit_hot >= 0 && const_bit_hot < var_width)
+ {
+ RTLIL::SigSpec var_high_sig(RTLIL::State::S0, var_width - const_bit_hot);
+ for (int i = const_bit_hot; i < var_width; i++) {
+ var_high_sig[i - const_bit_hot] = var_sig[i];
+ }
+
+ if (cmp_type == "$lt")
+ {
+ condition = stringf("unsigned X<%s", log_signal(const_sig));
+ replacement = stringf("!X[%d:%d]", var_width - 1, const_bit_hot);
+ module->addLogicNot(NEW_ID, var_high_sig, cell->getPort("\\Y"));
+ remove = true;
+ }
+ if (cmp_type == "$ge")
+ {
+ condition = stringf("unsigned X>=%s", log_signal(const_sig));
+ replacement = stringf("|X[%d:%d]", var_width - 1, const_bit_hot);
+ module->addReduceOr(NEW_ID, var_high_sig, cell->getPort("\\Y"));
+ remove = true;
+ }
}
- if (is_lt) {
- log("Replacing %s cell `%s' (implementing unsigned X<%s) with !X[%d:%d]: %s.\n",
- log_id(cell->type), log_id(cell), log_signal(sigConst), width - 1, bit_set, log_signal(a_prime));
- module->addLogicNot(NEW_ID, a_prime, cell->getPort("\\Y"));
- } else {
- log("Replacing %s cell `%s' (implementing unsigned X>=%s) with |X[%d:%d]: %s.\n",
- log_id(cell->type), log_id(cell), log_signal(sigConst), width - 1, bit_set, log_signal(a_prime));
- module->addReduceOr(NEW_ID, a_prime, cell->getPort("\\Y"));
+
+ int const_bit_set = get_highest_hot_index(const_sig);
+ if(const_bit_set >= var_width)
+ {
+ string cmp_name;
+ if (cmp_type == "$lt" || cmp_type == "$le")
+ {
+ if (cmp_type == "$lt") cmp_name = "<";
+ if (cmp_type == "$le") cmp_name = "<=";
+ condition = stringf("unsigned X[%d:0]%s%s", var_width - 1, cmp_name.c_str(), log_signal(const_sig));
+ replacement = "constant 1";
+ replace_sig[0] = State::S1;
+ replace = true;
+ }
+ if (cmp_type == "$gt" || cmp_type == "$ge")
+ {
+ if (cmp_type == "$gt") cmp_name = ">";
+ if (cmp_type == "$ge") cmp_name = ">=";
+ condition = stringf("unsigned X[%d:0]%s%s", var_width - 1, cmp_name.c_str(), log_signal(const_sig));
+ replacement = "constant 0";
+ replace_sig[0] = State::S0;
+ replace = true;
+ }
}
- module->remove(cell);
- did_something = true;
- goto next_cell;
}
- else if(const_bit_set >= width && const_bit_set >= 0){
- RTLIL::SigSpec a_prime(RTLIL::State::S0, 1);
- if(is_lt){
- a_prime[0] = RTLIL::State::S1;
- log("Replacing %s cell `%s' (implementing unsigned X[%d:0] < %s[%d:0]) with constant 0.\n", log_id(cell->type), log_id(cell), width-1, log_signal(sigConst),const_width-1);
+ else
+ { /* signed */
+ if (const_sig.is_fully_zero() && cmp_type == "$lt")
+ {
+ condition = "signed X<0";
+ replacement = stringf("X[%d]", var_width - 1);
+ replace_sig[0] = var_sig[var_width - 1];
+ replace = true;
}
- else{
- log("Replacing %s cell `%s' (implementing unsigned X[%d:0]>= %s[%d:0]) with constant 1.\n", log_id(cell->type), log_id(cell), width-1, log_signal(sigConst),const_width-1);
+ if (const_sig.is_fully_zero() && cmp_type == "$ge")
+ {
+ condition = "signed X>=0";
+ replacement = stringf("X[%d]", var_width - 1);
+ module->addNot(NEW_ID, var_sig[var_width - 1], cell->getPort("\\Y"));
+ remove = true;
}
- module->connect(cell->getPort("\\Y"), a_prime);
+ }
+
+ if (replace || remove)
+ {
+ log("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);
module->remove(cell);
did_something = true;
goto next_cell;
-
}
}
}
@@ -1477,7 +1521,7 @@ struct OptExprPass : public Pass {
log(" opt_expr [options] [selection]\n");
log("\n");
log("This pass performs const folding on internal cell types with constant inputs.\n");
- log("It also performs some simple expression rewritring.\n");
+ log("It also performs some simple expression rewriting.\n");
log("\n");
log(" -mux_undef\n");
log(" remove 'undef' inputs from $mux, $pmux and $_MUX_ cells\n");
diff --git a/passes/opt/opt_lut.cc b/passes/opt/opt_lut.cc
new file mode 100644
index 00000000..26855fd7
--- /dev/null
+++ b/passes/opt/opt_lut.cc
@@ -0,0 +1,607 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2018 whitequark <whitequark@whitequark.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+#include "kernel/modtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct OptLutWorker
+{
+ dict<IdString, dict<int, IdString>> &dlogic;
+ RTLIL::Module *module;
+ ModIndex index;
+ SigMap sigmap;
+
+ pool<RTLIL::Cell*> luts;
+ dict<RTLIL::Cell*, int> luts_arity;
+ dict<RTLIL::Cell*, pool<RTLIL::Cell*>> luts_dlogics;
+ dict<RTLIL::Cell*, pool<int>> luts_dlogic_inputs;
+
+ int eliminated_count = 0, combined_count = 0;
+
+ bool evaluate_lut(RTLIL::Cell *lut, dict<SigBit, bool> inputs)
+ {
+ SigSpec lut_input = sigmap(lut->getPort("\\A"));
+ int lut_width = lut->getParam("\\WIDTH").as_int();
+ Const lut_table = lut->getParam("\\LUT");
+ int lut_index = 0;
+
+ for (int i = 0; i < lut_width; i++)
+ {
+ SigBit input = sigmap(lut_input[i]);
+ if (inputs.count(input))
+ {
+ lut_index |= inputs[input] << i;
+ }
+ else
+ {
+ lut_index |= SigSpec(lut_input[i]).as_bool() << i;
+ }
+ }
+
+ return lut_table.extract(lut_index).as_bool();
+ }
+
+ void show_stats_by_arity()
+ {
+ dict<int, int> arity_counts;
+ dict<IdString, int> dlogic_counts;
+ int max_arity = 0;
+
+ for (auto lut_arity : luts_arity)
+ {
+ max_arity = max(max_arity, lut_arity.second);
+ arity_counts[lut_arity.second]++;
+ }
+
+ for (auto &lut_dlogics : luts_dlogics)
+ {
+ for (auto &lut_dlogic : lut_dlogics.second)
+ {
+ dlogic_counts[lut_dlogic->type]++;
+ }
+ }
+
+ log("Number of LUTs: %8zu\n", luts.size());
+ for (int arity = 1; arity <= max_arity; arity++)
+ {
+ if (arity_counts[arity])
+ log(" %d-LUT %16d\n", arity, arity_counts[arity]);
+ }
+ for (auto &dlogic_count : dlogic_counts)
+ {
+ log(" with %-12s %4d\n", dlogic_count.first.c_str(), dlogic_count.second);
+ }
+ }
+
+ OptLutWorker(dict<IdString, dict<int, IdString>> &dlogic, RTLIL::Module *module, int limit) :
+ dlogic(dlogic), module(module), index(module), sigmap(module)
+ {
+ log("Discovering LUTs.\n");
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type == "$lut")
+ {
+ int lut_width = cell->getParam("\\WIDTH").as_int();
+ SigSpec lut_input = cell->getPort("\\A");
+ int lut_arity = 0;
+
+ log("Found $lut\\WIDTH=%d cell %s.%s.\n", lut_width, log_id(module), log_id(cell));
+ luts.insert(cell);
+
+ // First, find all dedicated logic we're connected to. This results in an overapproximation
+ // of such connections.
+ pool<RTLIL::Cell*> lut_all_dlogics;
+ for (int i = 0; i < lut_width; i++)
+ {
+ SigBit bit = lut_input[i];
+ for (auto &port : index.query_ports(bit))
+ {
+ if (dlogic.count(port.cell->type))
+ {
+ auto &dlogic_map = dlogic[port.cell->type];
+ if (dlogic_map.count(i))
+ {
+ if (port.port == dlogic_map[i])
+ {
+ lut_all_dlogics.insert(port.cell);
+ }
+ }
+ }
+ }
+ }
+
+ // Second, make sure that the connection to dedicated logic is legal. If it is not legal,
+ // it means one of the two things:
+ // * The connection is spurious. I.e. this is dedicated logic that will be packed
+ // with some other LUT, and it just happens to be connected to this LUT as well.
+ // * The connection is illegal.
+ // In either of these cases, we don't need to concern ourselves with preserving the connection
+ // between this LUT and this dedicated logic cell.
+ pool<RTLIL::Cell*> lut_legal_dlogics;
+ pool<int> lut_dlogic_inputs;
+ for (auto lut_dlogic : lut_all_dlogics)
+ {
+ auto &dlogic_map = dlogic[lut_dlogic->type];
+ bool legal = true;
+ for (auto &dlogic_conn : dlogic_map)
+ {
+ if (lut_width <= dlogic_conn.first)
+ {
+ log(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
+ log(" LUT input A[%d] not present.\n", dlogic_conn.first);
+ legal = false;
+ break;
+ }
+ if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic->getPort(dlogic_conn.second)))
+ {
+ log(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
+ log(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic->getPort(dlogic_conn.second)));
+ legal = false;
+ break;
+ }
+ }
+
+ if (legal)
+ {
+ log(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic));
+ lut_legal_dlogics.insert(lut_dlogic);
+ for (auto &dlogic_conn : dlogic_map)
+ lut_dlogic_inputs.insert(dlogic_conn.first);
+ }
+ }
+
+ // Third, determine LUT arity. An n-wide LUT that has k constant inputs and m inputs shared with dedicated
+ // logic implements an (n-k-m)-ary function.
+ for (int i = 0; i < lut_width; i++)
+ {
+ SigBit bit = lut_input[i];
+ if (bit.wire || lut_dlogic_inputs.count(i))
+ lut_arity++;
+ }
+
+ log(" Cell implements a %d-LUT.\n", lut_arity);
+ luts_arity[cell] = lut_arity;
+ luts_dlogics[cell] = lut_legal_dlogics;
+ luts_dlogic_inputs[cell] = lut_dlogic_inputs;
+ }
+ }
+ show_stats_by_arity();
+
+ log("\n");
+ log("Eliminating LUTs.\n");
+ pool<RTLIL::Cell*> worklist = luts;
+ while (worklist.size())
+ {
+ if (limit == 0)
+ {
+ log("Limit reached.\n");
+ break;
+ }
+
+ auto lut = worklist.pop();
+ SigSpec lut_input = sigmap(lut->getPort("\\A"));
+ pool<int> &lut_dlogic_inputs = luts_dlogic_inputs[lut];
+
+ vector<SigBit> lut_inputs;
+ for (auto &bit : lut_input)
+ {
+ if (bit.wire)
+ lut_inputs.push_back(sigmap(bit));
+ }
+
+ bool const0_match = true;
+ bool const1_match = true;
+ vector<bool> input_matches;
+ for (size_t i = 0; i < lut_inputs.size(); i++)
+ input_matches.push_back(true);
+
+ for (int eval = 0; eval < 1 << lut_inputs.size(); eval++)
+ {
+ dict<SigBit, bool> eval_inputs;
+ for (size_t i = 0; i < lut_inputs.size(); i++)
+ eval_inputs[lut_inputs[i]] = (eval >> i) & 1;
+ bool value = evaluate_lut(lut, eval_inputs);
+ if (value != 0)
+ const0_match = false;
+ if (value != 1)
+ const1_match = false;
+ for (size_t i = 0; i < lut_inputs.size(); i++)
+ {
+ if (value != eval_inputs[lut_inputs[i]])
+ input_matches[i] = false;
+ }
+ }
+
+ int input_match = -1;
+ for (size_t i = 0; i < lut_inputs.size(); i++)
+ if (input_matches[i])
+ input_match = i;
+
+ if (const0_match || const1_match || input_match != -1)
+ {
+ log("Found redundant cell %s.%s.\n", log_id(module), log_id(lut));
+
+ SigBit value;
+ if (const0_match)
+ {
+ log(" Cell evaluates constant 0.\n");
+ value = State::S0;
+ }
+ if (const1_match)
+ {
+ log(" Cell evaluates constant 1.\n");
+ value = State::S1;
+ }
+ if (input_match != -1) {
+ log(" Cell evaluates signal %s.\n", log_signal(lut_inputs[input_match]));
+ value = lut_inputs[input_match];
+ }
+
+ if (lut_dlogic_inputs.size())
+ {
+ log(" Not eliminating cell (connected to dedicated logic).\n");
+ }
+ else
+ {
+ SigSpec lut_output = lut->getPort("\\Y");
+ for (auto &port : index.query_ports(lut_output))
+ {
+ if (port.cell != lut && luts.count(port.cell))
+ worklist.insert(port.cell);
+ }
+
+ module->connect(lut_output, value);
+ sigmap.add(lut_output, value);
+
+ module->remove(lut);
+ luts.erase(lut);
+ luts_arity.erase(lut);
+ luts_dlogics.erase(lut);
+ luts_dlogic_inputs.erase(lut);
+
+ eliminated_count++;
+ if (limit > 0)
+ limit--;
+ }
+ }
+ }
+ show_stats_by_arity();
+
+ log("\n");
+ log("Combining LUTs.\n");
+ worklist = luts;
+ while (worklist.size())
+ {
+ if (limit == 0)
+ {
+ log("Limit reached.\n");
+ break;
+ }
+
+ auto lutA = worklist.pop();
+ SigSpec lutA_input = sigmap(lutA->getPort("\\A"));
+ SigSpec lutA_output = sigmap(lutA->getPort("\\Y")[0]);
+ int lutA_width = lutA->getParam("\\WIDTH").as_int();
+ int lutA_arity = luts_arity[lutA];
+ pool<int> &lutA_dlogic_inputs = luts_dlogic_inputs[lutA];
+
+ auto lutA_output_ports = index.query_ports(lutA->getPort("\\Y"));
+ if (lutA_output_ports.size() != 2)
+ continue;
+
+ for (auto &port : lutA_output_ports)
+ {
+ if (port.cell == lutA)
+ continue;
+
+ if (luts.count(port.cell))
+ {
+ auto lutB = port.cell;
+ SigSpec lutB_input = sigmap(lutB->getPort("\\A"));
+ SigSpec lutB_output = sigmap(lutB->getPort("\\Y")[0]);
+ int lutB_width = lutB->getParam("\\WIDTH").as_int();
+ int lutB_arity = luts_arity[lutB];
+ pool<int> &lutB_dlogic_inputs = luts_dlogic_inputs[lutB];
+
+ log("Found %s.%s (cell A) feeding %s.%s (cell B).\n", log_id(module), log_id(lutA), log_id(module), log_id(lutB));
+
+ if (index.query_is_output(lutA->getPort("\\Y")))
+ {
+ log(" Not combining LUTs (cascade connection feeds module output).\n");
+ continue;
+ }
+
+ pool<SigBit> lutA_inputs;
+ pool<SigBit> lutB_inputs;
+ for (auto &bit : lutA_input)
+ {
+ if (bit.wire)
+ lutA_inputs.insert(sigmap(bit));
+ }
+ for (auto &bit : lutB_input)
+ {
+ if (bit.wire)
+ lutB_inputs.insert(sigmap(bit));
+ }
+
+ pool<SigBit> common_inputs;
+ for (auto &bit : lutA_inputs)
+ {
+ if (lutB_inputs.count(bit))
+ common_inputs.insert(bit);
+ }
+
+ 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());
+ 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());
+ 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);
+
+ const int COMBINE_A = 1, COMBINE_B = 2, COMBINE_EITHER = COMBINE_A | COMBINE_B;
+ int combine_mask = 0;
+ if (lutM_arity > lutA_width)
+ {
+ log(" Not combining LUTs into cell A (combined LUT wider than cell A).\n");
+ }
+ else if (lutB_dlogic_inputs.size() > 0)
+ {
+ log(" Not combining LUTs into cell A (cell B is connected to dedicated logic).\n");
+ }
+ else if (lutB->get_bool_attribute("\\lut_keep"))
+ {
+ log(" Not combining LUTs into cell A (cell B has attribute \\lut_keep).\n");
+ }
+ else
+ {
+ combine_mask |= COMBINE_A;
+ }
+ if (lutM_arity > lutB_width)
+ {
+ log(" Not combining LUTs into cell B (combined LUT wider than cell B).\n");
+ }
+ else if (lutA_dlogic_inputs.size() > 0)
+ {
+ log(" Not combining LUTs into cell B (cell A is connected to dedicated logic).\n");
+ }
+ else if (lutA->get_bool_attribute("\\lut_keep"))
+ {
+ log(" Not combining LUTs into cell B (cell A has attribute \\lut_keep).\n");
+ }
+ else
+ {
+ combine_mask |= COMBINE_B;
+ }
+
+ int combine = combine_mask;
+ if (combine == COMBINE_EITHER)
+ {
+ log(" Can combine into either cell.\n");
+ if (lutA_arity == 1)
+ {
+ log(" Cell A is a buffer or inverter, combining into cell B.\n");
+ combine = COMBINE_B;
+ }
+ else if (lutB_arity == 1)
+ {
+ log(" Cell B is a buffer or inverter, combining into cell A.\n");
+ combine = COMBINE_A;
+ }
+ else
+ {
+ log(" Arbitrarily combining into cell A.\n");
+ combine = COMBINE_A;
+ }
+ }
+
+ RTLIL::Cell *lutM, *lutR;
+ pool<SigBit> lutM_inputs, lutR_inputs;
+ pool<int> lutM_dlogic_inputs;
+ if (combine == COMBINE_A)
+ {
+ log(" Combining LUTs into cell A.\n");
+ lutM = lutA;
+ lutM_inputs = lutA_inputs;
+ lutM_dlogic_inputs = lutA_dlogic_inputs;
+ lutR = lutB;
+ lutR_inputs = lutB_inputs;
+ }
+ else if (combine == COMBINE_B)
+ {
+ log(" Combining LUTs into cell B.\n");
+ lutM = lutB;
+ lutM_inputs = lutB_inputs;
+ lutM_dlogic_inputs = lutB_dlogic_inputs;
+ lutR = lutA;
+ lutR_inputs = lutA_inputs;
+ }
+ else
+ {
+ log(" Cannot combine LUTs.\n");
+ continue;
+ }
+
+ pool<SigBit> lutR_unique;
+ for (auto &bit : lutR_inputs)
+ {
+ if (!common_inputs.count(bit) && bit != lutA_output)
+ lutR_unique.insert(bit);
+ }
+
+ int lutM_width = lutM->getParam("\\WIDTH").as_int();
+ SigSpec lutM_input = sigmap(lutM->getPort("\\A"));
+ std::vector<SigBit> lutM_new_inputs;
+ for (int i = 0; i < lutM_width; i++)
+ {
+ bool input_unused = false;
+ if (sigmap(lutM_input[i]) == lutA_output)
+ input_unused = true;
+ if (!lutM_input[i].wire && !lutM_dlogic_inputs.count(i))
+ input_unused = true;
+
+ if (input_unused && lutR_unique.size())
+ {
+ SigBit new_input = lutR_unique.pop();
+ log(" Connecting input %d as %s.\n", i, log_signal(new_input));
+ lutM_new_inputs.push_back(new_input);
+ }
+ else if (sigmap(lutM_input[i]) == lutA_output)
+ {
+ log(" Disconnecting cascade input %d.\n", i);
+ lutM_new_inputs.push_back(SigBit());
+ }
+ else
+ {
+ log(" Leaving input %d as %s.\n", i, log_signal(lutM_input[i]));
+ lutM_new_inputs.push_back(lutM_input[i]);
+ }
+ }
+ log_assert(lutR_unique.size() == 0);
+
+ RTLIL::Const lutM_new_table(State::Sx, 1 << lutM_width);
+ for (int eval = 0; eval < 1 << lutM_width; eval++)
+ {
+ dict<SigBit, bool> eval_inputs;
+ for (size_t i = 0; i < lutM_new_inputs.size(); i++)
+ {
+ eval_inputs[lutM_new_inputs[i]] = (eval >> i) & 1;
+ }
+ eval_inputs[lutA_output] = evaluate_lut(lutA, eval_inputs);
+ lutM_new_table[eval] = (RTLIL::State) evaluate_lut(lutB, eval_inputs);
+ }
+
+ log(" Cell A truth table: %s.\n", lutA->getParam("\\LUT").as_string().c_str());
+ log(" Cell B truth table: %s.\n", lutB->getParam("\\LUT").as_string().c_str());
+ log(" Merged truth table: %s.\n", lutM_new_table.as_string().c_str());
+
+ lutM->setParam("\\LUT", lutM_new_table);
+ lutM->setPort("\\A", lutM_new_inputs);
+ lutM->setPort("\\Y", lutB_output);
+
+ luts_arity[lutM] = lutM_arity;
+ luts.erase(lutR);
+ luts_arity.erase(lutR);
+ lutR->module->remove(lutR);
+
+ worklist.insert(lutM);
+ worklist.erase(lutR);
+
+ combined_count++;
+ if (limit > 0)
+ limit--;
+ }
+ }
+ }
+ show_stats_by_arity();
+ }
+};
+
+static void split(std::vector<std::string> &tokens, const std::string &text, char sep)
+{
+ size_t start = 0, end = 0;
+ while ((end = text.find(sep, start)) != std::string::npos) {
+ tokens.push_back(text.substr(start, end - start));
+ start = end + 1;
+ }
+ tokens.push_back(text.substr(start));
+}
+
+struct OptLutPass : public Pass {
+ OptLutPass() : Pass("opt_lut", "optimize LUT cells") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_lut [options] [selection]\n");
+ log("\n");
+ log("This pass combines cascaded $lut cells with unused inputs.\n");
+ log("\n");
+ log(" -dlogic <type>:<cell-port>=<LUT-input>[:<cell-port>=<LUT-input>...]\n");
+ log(" preserve connections to dedicated logic cell <type> that has ports\n");
+ log(" <cell-port> connected to LUT inputs <LUT-input>. this includes\n");
+ log(" the case where both LUT and dedicated logic input are connected to\n");
+ log(" the same constant.\n");
+ log("\n");
+ log(" -limit N\n");
+ log(" only perform the first N combines, then stop. useful for debugging.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing OPT_LUT pass (optimize LUTs).\n");
+
+ dict<IdString, dict<int, IdString>> dlogic;
+ int limit = -1;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-dlogic" && argidx+1 < args.size())
+ {
+ std::vector<std::string> tokens;
+ split(tokens, args[++argidx], ':');
+ if (tokens.size() < 2)
+ log_cmd_error("The -dlogic option requires at least one connection.\n");
+ IdString type = "\\" + tokens[0];
+ for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) {
+ std::vector<std::string> conn_tokens;
+ split(conn_tokens, *it, '=');
+ if (conn_tokens.size() != 2)
+ log_cmd_error("Invalid format of -dlogic signal mapping.\n");
+ IdString logic_port = "\\" + conn_tokens[0];
+ int lut_input = atoi(conn_tokens[1].c_str());
+ dlogic[type][lut_input] = logic_port;
+ }
+ continue;
+ }
+ if (args[argidx] == "-limit" && argidx + 1 < args.size())
+ {
+ limit = atoi(args[++argidx].c_str());
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ int eliminated_count = 0, combined_count = 0;
+ for (auto module : design->selected_modules())
+ {
+ OptLutWorker worker(dlogic, module, limit - eliminated_count - combined_count);
+ eliminated_count += worker.eliminated_count;
+ combined_count += worker.combined_count;
+ }
+ if (eliminated_count)
+ design->scratchpad_set_bool("opt.did_something", true);
+ if (combined_count)
+ design->scratchpad_set_bool("opt.did_something", true);
+ log("\n");
+ log("Eliminated %d LUTs.\n", eliminated_count);
+ log("Combined %d LUTs.\n", combined_count);
+ }
+} OptLutPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc
index 87c7ce9b..375697dc 100644
--- a/passes/opt/opt_muxtree.cc
+++ b/passes/opt/opt_muxtree.cc
@@ -36,6 +36,7 @@ struct OptMuxtreeWorker
RTLIL::Module *module;
SigMap assign_map;
int removed_count;
+ int glob_abort_cnt = 100000;
struct bitinfo_t {
bool seen_non_mux;
@@ -293,6 +294,9 @@ struct OptMuxtreeWorker
void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx, bool do_replace_known, bool do_enable_ports, int abort_count)
{
+ if (glob_abort_cnt == 0)
+ return;
+
muxinfo_t &muxinfo = mux2info[mux_idx];
if (do_enable_ports)
@@ -315,7 +319,7 @@ struct OptMuxtreeWorker
knowledge.visited_muxes[m] = true;
parent_muxes.push_back(m);
}
- for (int m : parent_muxes)
+ for (int m : parent_muxes) {
if (root_enable_muxes.at(m))
continue;
else if (root_muxes.at(m)) {
@@ -327,6 +331,9 @@ struct OptMuxtreeWorker
eval_mux(knowledge, m, false, do_enable_ports, abort_count - 1);
} else
eval_mux(knowledge, m, do_replace_known, do_enable_ports, abort_count);
+ if (glob_abort_cnt == 0)
+ return;
+ }
for (int m : parent_muxes)
knowledge.visited_muxes[m] = false;
@@ -390,6 +397,12 @@ 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");
+ return;
+ }
+ glob_abort_cnt--;
+
muxinfo_t &muxinfo = mux2info[mux_idx];
// set input ports to constants if we find known active or inactive signals
@@ -433,6 +446,9 @@ struct OptMuxtreeWorker
if (knowledge.known_inactive.at(portinfo.ctrl_sig))
continue;
eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count);
+
+ if (glob_abort_cnt == 0)
+ return;
}
}
diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc
index 5880254c..e8570f0e 100644
--- a/passes/opt/opt_rmdff.cc
+++ b/passes/opt/opt_rmdff.cc
@@ -174,8 +174,6 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
cell->unsetParam("\\CLR_POLARITY");
cell->unsetPort("\\SET");
cell->unsetPort("\\CLR");
-
- return true;
}
else
{
@@ -186,11 +184,12 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
cell->unsetParam("\\CLR_POLARITY");
cell->unsetPort("\\SET");
cell->unsetPort("\\CLR");
-
- return true;
}
+
+ return true;
}
- else
+
+ if (!hasreset)
{
IdString new_type;
@@ -207,8 +206,10 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell)
cell->unsetPort("\\S");
cell->unsetPort("\\R");
- return did_something;
+ return true;
}
+
+ return did_something;
}
bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch)
diff --git a/passes/opt/share.cc b/passes/opt/share.cc
index b8028082..c85c2742 100644
--- a/passes/opt/share.cc
+++ b/passes/opt/share.cc
@@ -710,8 +710,12 @@ struct ShareWorker
RTLIL::Cell *supercell = module->addCell(NEW_ID, c1);
RTLIL::SigSpec addr1 = c1->getPort("\\ADDR");
RTLIL::SigSpec addr2 = c2->getPort("\\ADDR");
- if (addr1 != addr2)
- supercell->setPort("\\ADDR", module->Mux(NEW_ID, addr2, addr1, act));
+ if (GetSize(addr1) < GetSize(addr2))
+ addr1.extend_u0(GetSize(addr2));
+ else
+ addr2.extend_u0(GetSize(addr1));
+ supercell->setPort("\\ADDR", addr1 != addr2 ? module->Mux(NEW_ID, addr2, addr1, act) : addr1);
+ supercell->parameters["\\ABITS"] = RTLIL::Const(GetSize(addr1));
supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort("\\DATA"), c2->getPort("\\DATA")));
supercell_aux.insert(supercell);
return supercell;
diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc
index 0164f58d..52245ce3 100644
--- a/passes/opt/wreduce.cc
+++ b/passes/opt/wreduce.cc
@@ -38,7 +38,8 @@ struct WreduceConfig
"$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
"$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt",
"$add", "$sub", "$mul", // "$div", "$mod", "$pow",
- "$mux", "$pmux"
+ "$mux", "$pmux",
+ "$dff", "$adff"
});
}
};
@@ -52,6 +53,8 @@ struct WreduceWorker
std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells;
std::set<SigBit> work_queue_bits;
pool<SigBit> keep_bits;
+ dict<SigBit, State> init_bits;
+ pool<SigBit> remove_init_bits;
WreduceWorker(WreduceConfig *config, Module *module) :
config(config), module(module), mi(module) { }
@@ -134,6 +137,91 @@ struct WreduceWorker
module->connect(sig_y.extract(n_kept, n_removed), sig_removed);
}
+ void run_cell_dff(Cell *cell)
+ {
+ // Reduce size of FF if inputs are just sign/zero extended or output bit is not used
+
+ SigSpec sig_d = mi.sigmap(cell->getPort("\\D"));
+ SigSpec sig_q = mi.sigmap(cell->getPort("\\Q"));
+ Const initval;
+
+ int width_before = GetSize(sig_q);
+
+ if (width_before == 0)
+ return;
+
+ bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0;
+ bool sign_ext = !zero_ext;
+
+ for (int i = 0; i < GetSize(sig_q); i++) {
+ SigBit bit = sig_q[i];
+ if (init_bits.count(bit))
+ initval.bits.push_back(init_bits.at(bit));
+ else
+ initval.bits.push_back(State::Sx);
+ }
+
+ for (int i = GetSize(sig_q)-1; i >= 0; i--)
+ {
+ if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || initval[i] == State::Sx)) {
+ module->connect(sig_q[i], State::S0);
+ remove_init_bits.insert(sig_q[i]);
+ sig_d.remove(i);
+ sig_q.remove(i);
+ continue;
+ }
+
+ if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1]) {
+ module->connect(sig_q[i], sig_q[i-1]);
+ remove_init_bits.insert(sig_q[i]);
+ sig_d.remove(i);
+ sig_q.remove(i);
+ continue;
+ }
+
+ auto info = mi.query(sig_q[i]);
+ 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);
+ sig_q.remove(i);
+ zero_ext = false;
+ sign_ext = false;
+ continue;
+ }
+
+ break;
+ }
+
+ if (width_before == GetSize(sig_q))
+ return;
+
+ if (GetSize(sig_q) == 0) {
+ log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type));
+ module->remove(cell);
+ return;
+ }
+
+ log("Removed top %d bits (of %d) from FF cell %s.%s (%s).\n", width_before - GetSize(sig_q), width_before,
+ log_id(module), log_id(cell), log_id(cell->type));
+
+ for (auto bit : sig_d)
+ work_queue_bits.insert(bit);
+
+ for (auto bit : sig_q)
+ work_queue_bits.insert(bit);
+
+ // Narrow ARST_VALUE parameter to new size.
+ if (cell->parameters.count("\\ARST_VALUE")) {
+ Const arst_value = cell->getParam("\\ARST_VALUE");
+ arst_value.bits.resize(GetSize(sig_q));
+ cell->setParam("\\ARST_VALUE", arst_value);
+ }
+
+ cell->setPort("\\D", sig_d);
+ cell->setPort("\\Q", sig_q);
+ cell->fixup_parameters();
+ }
+
void run_reduce_inport(Cell *cell, char port, int max_port_size, bool &port_signed, bool &did_something)
{
port_signed = cell->getParam(stringf("\\%c_SIGNED", port)).as_bool();
@@ -176,6 +264,9 @@ struct WreduceWorker
if (cell->type.in("$mux", "$pmux"))
return run_cell_mux(cell);
+ if (cell->type.in("$dff", "$adff"))
+ return run_cell_dff(cell);
+
SigSpec sig = mi.sigmap(cell->getPort("\\Y"));
if (sig.has_const())
@@ -235,8 +326,11 @@ struct WreduceWorker
} else {
while (GetSize(sig) > 0)
{
- auto info = mi.query(sig[GetSize(sig)-1]);
+ auto bit = sig[GetSize(sig)-1];
+ if (keep_bits.count(bit))
+ break;
+ auto info = mi.query(bit);
if (info->is_output || GetSize(info->ports) > 1)
break;
@@ -297,10 +391,21 @@ struct WreduceWorker
void run()
{
- for (auto w : module->wires())
+ // create a copy as mi.sigmap will be updated as we process the module
+ SigMap init_attr_sigmap = mi.sigmap;
+
+ for (auto w : module->wires()) {
if (w->get_bool_attribute("\\keep"))
for (auto bit : mi.sigmap(w))
keep_bits.insert(bit);
+ if (w->attributes.count("\\init")) {
+ Const initval = w->attributes.at("\\init");
+ SigSpec initsig = init_attr_sigmap(w);
+ int width = std::min(GetSize(initval), GetSize(initsig));
+ for (int i = 0; i < width; i++)
+ init_bits[initsig[i]] = initval[i];
+ }
+ }
for (auto c : module->selected_cells())
work_queue_cells.insert(c);
@@ -348,6 +453,24 @@ struct WreduceWorker
module->connect(nw, SigSpec(w).extract(0, GetSize(nw)));
module->swap_names(w, nw);
}
+
+ if (!remove_init_bits.empty()) {
+ for (auto w : module->wires()) {
+ if (w->attributes.count("\\init")) {
+ Const initval = w->attributes.at("\\init");
+ Const new_initval(State::Sx, GetSize(w));
+ 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);
+ }
+ }
+ }
}
};
diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore
new file mode 100644
index 00000000..c9263057
--- /dev/null
+++ b/passes/pmgen/.gitignore
@@ -0,0 +1 @@
+/ice40_dsp_pm.h
diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc
new file mode 100644
index 00000000..e0609d9b
--- /dev/null
+++ b/passes/pmgen/Makefile.inc
@@ -0,0 +1,8 @@
+OBJS += passes/pmgen/ice40_dsp.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 $^ $@
diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md
new file mode 100644
index 00000000..223b4305
--- /dev/null
+++ b/passes/pmgen/README.md
@@ -0,0 +1,224 @@
+Pattern Matcher Generator
+=========================
+
+The program `pmgen.py` reads a `.pmg` (Pattern Matcher Generator) file and
+writes a header-only C++ library that implements that pattern matcher.
+
+The "patterns" in this context are subgraphs in a Yosys RTLIL netlist.
+
+The algorithm used in the generated pattern matcher is a simple recursive
+search with backtracking. It is left to the author of the `.pmg` file to
+determine an efficient cell order for the search that allows for maximum
+use of indices and early backtracking.
+
+
+API of Generated Matcher
+========================
+
+When `pmgen.py` reads a `foobar.pmg` file, it writes `foobar_pm.h` containing
+a class `foobar_pm`. That class is instantiated with an RTLIL module and a
+list of cells from that module:
+
+ foobar_pm pm(module, module->selected_cells());
+
+The caller must make sure that none of the cells in the 2nd argument are
+deleted for as long as the patter matcher instance is used.
+
+At any time it is possible to disable cells, preventing them from showing
+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:
+
+ pm.run([&](){
+ 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`.)
+
+Similarly the `.pmg` file declares user data variables that become members of
+`.ud`, a struct of type `foobar_pm::udata_t`.
+
+
+The .pmg File Format
+====================
+
+The `.pmg` file format is a simple line-based file format. For the most part
+lines consist of whitespace-separated tokens.
+
+Lines in `.pmg` files starting with `//` are comments.
+
+Declaring state variables
+-------------------------
+
+One or more state variables can be declared using the `state` statement,
+followed by a C++ type in angle brackets, followed by a whitespace separated
+list of variable names. For example:
+
+ state <bool> flag1 flag2 happy big
+ state <SigSpec> sigA sigB sigY
+
+State variables are automatically managed by the generated backtracking algorithm
+and saved and restored as needed.
+
+They are automatically initialized to the default constructed value of their type
+when `.run(callback_function)` is called.
+
+Declaring udata variables
+-------------------------
+
+Udata (user-data) variables can be used for example to configure the matcher or
+the callback function used to perform actions on found matches.
+
+There is no automatic management of udata variables. For this reason it is
+recommended that the user-supplied matcher code treats them as read-only
+variables.
+
+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.
+
+Embedded C++ code
+-----------------
+
+Many statements in a `.pmg` file contain C++ code. However, there are some
+slight additions to regular C++/Yosys/RTLIL code that make it a bit easier to
+write matchers:
+
+- Identifiers starting with a dollar sign or backslash are automatically
+ converted to special IdString variables that are initialized when the
+ matcher object is constructed.
+
+- The `port(<cell>, <portname>)` function is a handy alias for
+ `sigmap(<cell>->getPort(<portname>))`.
+
+- Similarly `param(<cell>, <paramname>)` looks up a parameter on a cell.
+
+- The function `nusers(<sigspec>)` returns the number of different cells
+ connected to any of the given signal bits, plus one if any of the signal
+ bits is also a primary input or primary output.
+
+- In `code..endcode` blocks there exist `accept`, `reject`, and `branch`
+ statements.
+
+- In `index` statements there is a special `===` operator for the index
+ lookup.
+
+Matching cells
+--------------
+
+Cells are matched using `match..endmatch` blocks. For example:
+
+ match mul
+ if ff
+ select mul->type == $mul
+ select nusers(port(mul, \Y) == 2
+ index <SigSpec> port(mul, \Y) === port(ff, \D)
+ filter some_weird_function(mul) < other_weird_function(ff)
+ optional
+ endmatch
+
+A `match` block starts with `match <statevar>` and implicitly generates
+a state variable `<statevar>` of type `RTLIL::Cell*`.
+
+All statements in the match block are optional. (An empty match block
+would simply match each and every cell in the module.)
+
+The `if <expression>` statement makes the match block conditional. If
+`<expression>` evaluates to `false` then the match block will be ignored
+and the corresponding state variable is set to `nullptr`. In our example
+we only try to match the `mul` cell if the `ff` state variable points
+to a cell. (Presumably `ff` is provided by a prior `match` block.)
+
+The `select` lines are evaluated once for each cell when the matcher is
+initialized. A `match` block will only consider cells for which all `select`
+expressions evaluated to `true`. Note that the state variable corresponding to
+the match (in the example `mul`) is the only state variable that may be used
+in `select` lines.
+
+Index lines are using the `index <type> expr1 === expr2` syntax. `expr1` is
+evaluated during matcher initialization and the same restrictions apply as for
+`select` expressions. `expr2` is evaluated when the match is calulated. It is a
+function of any state variables assigned to by previous blocks. Both expression
+are converted to the given type and compared for equality. Only cells for which
+all `index` statements in the block pass are considered by the match.
+
+Note that `select` and `index` are fast operations. Thus `select` and `index`
+should be used whenever possible to create efficient matchers.
+
+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
+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`.
+
+Additional code
+---------------
+
+Interleaved with `match..endmatch` blocks there may be `code..endcode` blocks.
+Such a block starts with the keyword `code` followed by a list of state variables
+that the block may modify. For example:
+
+ code addAB sigS
+ if (addA) {
+ addAB = addA;
+ sigS = port(addA, \B);
+ }
+ if (addB) {
+ addAB = addB;
+ sigS = port(addB, \A);
+ }
+ endcode
+
+The special keyword `reject` can be used to reject the current state and
+backtrack. For example:
+
+ code
+ if (ffA && ffB) {
+ if (port(ffA, \CLK) != port(ffB, \CLK))
+ reject;
+ if (param(ffA, \CLK_POLARITY) != param(ffB, \CLK_POLARITY))
+ reject;
+ }
+ endcode
+
+Similarly, the special keyword `accept` can be used to accept the current
+state. (`accept` will not backtrack. This means it continues with the current
+branch and may accept a larger match later.)
+
+The special keyword `branch` can be used to explore different cases. Note that
+each code block has an implicit `branch` at the end. So most use-cases of the
+`branch` keyword need to end the block with `reject` to avoid the implicit
+branch at the end. For example:
+
+ state <int> mode
+
+ code mode
+ for (mode = 0; mode < 8; mode++)
+ branch;
+ reject;
+ endcode
+
+But in some cases it is more natural to utilize the implicit branch statement:
+
+ state <IdString> portAB
+
+ code portAB
+ portAB = \A;
+ branch;
+ portAB = \B;
+ endcode
+
+There is an implicit `code..endcode` block at the end of each `.pgm` 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
new file mode 100644
index 00000000..3a054a46
--- /dev/null
+++ b/passes/pmgen/ice40_dsp.cc
@@ -0,0 +1,237 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+#include "passes/pmgen/ice40_dsp_pm.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void create_ice40_dsp(ice40_dsp_pm &pm)
+{
+#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, "--"));
+#endif
+
+ log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.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));
+ 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));
+ 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));
+ 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));
+ return;
+ }
+
+ bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool();
+
+ if (mul_signed) {
+ log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n");
+ return;
+ }
+
+ 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);
+
+ // SB_MAC16 Input Interface
+
+ SigSpec A = pm.st.sigA;
+ A.extend_u0(16, mul_signed);
+
+ SigSpec B = pm.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");
+ CD.extend_u0(32, mul_signed);
+
+ cell->setPort("\\A", A);
+ cell->setPort("\\B", B);
+ 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->setPort("\\AHOLD", State::S0);
+ cell->setPort("\\BHOLD", State::S0);
+ cell->setPort("\\CHOLD", State::S0);
+ cell->setPort("\\DHOLD", State::S0);
+
+ cell->setPort("\\IRSTTOP", State::S0);
+ cell->setPort("\\IRSTBOT", State::S0);
+
+ if (pm.st.clock_vld)
+ {
+ cell->setPort("\\CLK", pm.st.clock);
+ cell->setPort("\\CE", State::S1);
+ cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1);
+
+ log(" clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge");
+
+ if (pm.st.ffA)
+ log(" ffA:%s", log_id(pm.st.ffA));
+
+ if (pm.st.ffB)
+ log(" ffB:%s", log_id(pm.st.ffB));
+
+ if (pm.st.ffY)
+ log(" ffY:%s", log_id(pm.st.ffY));
+
+ if (pm.st.ffS)
+ log(" ffS:%s", log_id(pm.st.ffS));
+
+ log("\n");
+ }
+ else
+ {
+ cell->setPort("\\CLK", State::S0);
+ cell->setPort("\\CE", State::S0);
+ cell->setParam("\\NEG_TRIGGER", State::S0);
+ }
+
+ // SB_MAC16 Cascade Interface
+
+ cell->setPort("\\SIGNEXTIN", State::Sx);
+ cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID));
+
+ cell->setPort("\\CI", State::Sx);
+ cell->setPort("\\CO", pm.module->addWire(NEW_ID));
+
+ cell->setPort("\\ACCUMCI", State::Sx);
+ cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID));
+
+ // SB_MAC16 Output Interface
+
+ SigSpec O = pm.st.ffS ? pm.st.sigS : pm.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);
+ } else {
+ cell->setPort("\\ADDSUBTOP", State::S0);
+ cell->setPort("\\ADDSUBBOT", State::S0);
+ }
+
+ cell->setPort("\\ORSTTOP", State::S0);
+ cell->setPort("\\ORSTBOT", State::S0);
+
+ cell->setPort("\\OHOLDTOP", State::S0);
+ 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"));
+
+ cell->setPort("\\OLOADTOP", acc_reset);
+ cell->setPort("\\OLOADBOT", acc_reset);
+
+ // SB_MAC16 Remaining Parameters
+
+ 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("\\PIPELINE_16x16_MULT_REG2", State::S0);
+
+ cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.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("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
+ cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
+ cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
+
+ cell->setParam("\\MODE_8x8", State::S0);
+ 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);
+}
+
+struct Ice40DspPass : public Pass {
+ Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" ice40_dsp [options] [selection]\n");
+ log("\n");
+ log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing ICE40_DSP pass (map multipliers).\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())
+ ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp);
+ }
+} Ice40DspPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg
new file mode 100644
index 00000000..96c62e31
--- /dev/null
+++ b/passes/pmgen/ice40_dsp.pmg
@@ -0,0 +1,160 @@
+state <SigBit> clock
+state <bool> clock_pol clock_vld
+state <SigSpec> sigA sigB sigY sigS
+state <Cell*> addAB muxAB
+
+match mul
+ select mul->type.in($mul)
+ select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10
+ select GetSize(mul->getPort(\Y)) > 10
+endmatch
+
+match ffA
+ select ffA->type.in($dff)
+ // select nusers(port(ffA, \Q)) == 2
+ index <SigSpec> port(ffA, \Q) === port(mul, \A)
+ optional
+endmatch
+
+code sigA clock clock_pol clock_vld
+ sigA = port(mul, \A);
+
+ if (ffA) {
+ sigA = port(ffA, \D);
+
+ clock = port(ffA, \CLK).as_bit();
+ clock_pol = param(ffA, \CLK_POLARITY).as_bool();
+ clock_vld = true;
+ }
+endcode
+
+match ffB
+ select ffB->type.in($dff)
+ // select nusers(port(ffB, \Q)) == 2
+ index <SigSpec> port(ffB, \Q) === port(mul, \B)
+ optional
+endmatch
+
+code sigB clock clock_pol clock_vld
+ sigB = port(mul, \B);
+
+ if (ffB) {
+ sigB = port(ffB, \D);
+ SigBit c = port(ffB, \CLK).as_bit();
+ bool cp = param(ffB, \CLK_POLARITY).as_bool();
+
+ if (clock_vld && (c != clock || cp != clock_pol))
+ reject;
+
+ clock = c;
+ clock_pol = cp;
+ clock_vld = true;
+ }
+endcode
+
+match ffY
+ select ffY->type.in($dff)
+ select nusers(port(ffY, \D)) == 2
+ index <SigSpec> port(ffY, \D) === port(mul, \Y)
+ optional
+endmatch
+
+code sigY clock clock_pol clock_vld
+ sigY = port(mul, \Y);
+
+ if (ffY) {
+ sigY = port(ffY, \Q);
+ SigBit c = port(ffY, \CLK).as_bit();
+ bool cp = param(ffY, \CLK_POLARITY).as_bool();
+
+ if (clock_vld && (c != clock || cp != clock_pol))
+ reject;
+
+ clock = c;
+ clock_pol = cp;
+ clock_vld = true;
+ }
+endcode
+
+match addA
+ select addA->type.in($add)
+ select nusers(port(addA, \A)) == 2
+ index <SigSpec> port(addA, \A) === sigY
+ optional
+endmatch
+
+match addB
+ if !addA
+ select addB->type.in($add, $sub)
+ select nusers(port(addB, \B)) == 2
+ index <SigSpec> port(addB, \B) === sigY
+ optional
+endmatch
+
+code addAB sigS
+ if (addA) {
+ addAB = addA;
+ sigS = port(addA, \B);
+ }
+ if (addB) {
+ addAB = addB;
+ sigS = port(addB, \A);
+ }
+ if (addAB) {
+ int natural_mul_width = GetSize(sigA) + GetSize(sigB);
+ int actual_mul_width = GetSize(sigY);
+ int actual_acc_width = GetSize(sigS);
+
+ if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width))
+ reject;
+ if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool()))
+ reject;
+ }
+endcode
+
+match muxA
+ if addAB
+ select muxA->type.in($mux)
+ select nusers(port(muxA, \A)) == 2
+ index <SigSpec> port(muxA, \A) === port(addAB, \Y)
+ optional
+endmatch
+
+match muxB
+ if addAB
+ if !muxA
+ select muxB->type.in($mux)
+ select nusers(port(muxB, \B)) == 2
+ index <SigSpec> port(muxB, \B) === port(addAB, \Y)
+ optional
+endmatch
+
+code muxAB
+ muxAB = addAB;
+ if (muxA)
+ muxAB = muxA;
+ if (muxB)
+ muxAB = muxB;
+endcode
+
+match ffS
+ if muxAB
+ select ffS->type.in($dff)
+ select nusers(port(ffS, \D)) == 2
+ index <SigSpec> port(ffS, \D) === port(muxAB, \Y)
+ index <SigSpec> port(ffS, \Q) === sigS
+endmatch
+
+code clock clock_pol clock_vld
+ if (ffS) {
+ SigBit c = port(ffS, \CLK).as_bit();
+ bool cp = param(ffS, \CLK_POLARITY).as_bool();
+
+ if (clock_vld && (c != clock || cp != clock_pol))
+ reject;
+
+ clock = c;
+ clock_pol = cp;
+ clock_vld = true;
+ }
+endcode
diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py
new file mode 100644
index 00000000..d9747b06
--- /dev/null
+++ b/passes/pmgen/pmgen.py
@@ -0,0 +1,486 @@
+#!/usr/bin/env python3
+
+import re
+import sys
+import pprint
+
+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]
+
+state_types = dict()
+udata_types = dict()
+blocks = list()
+ids = dict()
+
+def rewrite_cpp(s):
+ t = list()
+ i = 0
+ while i < len(s):
+ if s[i] in ("'", '"') and i + 1 < len(s):
+ j = i + 1
+ while j + 1 < len(s) and s[j] != s[i]:
+ if s[j] == '\\' and j + 1 < len(s):
+ j += 1
+ j += 1
+ t.append(s[i:j+1])
+ i = j + 1
+ continue
+
+ if s[i] in ('$', '\\') and i + 1 < len(s):
+ j = i + 1
+ while True:
+ if j == len(s):
+ j -= 1
+ break
+ if ord('a') <= ord(s[j]) <= ord('z'):
+ j += 1
+ continue
+ if ord('A') <= ord(s[j]) <= ord('Z'):
+ j += 1
+ continue
+ if ord('0') <= ord(s[j]) <= ord('9'):
+ j += 1
+ continue
+ if s[j] == '_':
+ j += 1
+ continue
+ j -= 1
+ break
+
+ n = s[i:j+1]
+ i = j + 1
+
+ if n[0] == '$':
+ v = "id_d_" + n[1:]
+ else:
+ v = "id_b_" + n[1:]
+
+ if v not in ids:
+ ids[v] = n
+ else:
+ assert ids[v] == n
+
+ t.append(v)
+ continue
+
+ if s[i] == "\t":
+ t.append(" ")
+ else:
+ t.append(s[i])
+
+ i += 1
+
+ return "".join(t)
+
+with open(pmgfile, "r") as f:
+ while True:
+ line = f.readline()
+ if line == "": break
+ line = line.strip()
+
+ cmd = line.split()
+ if len(cmd) == 0 or cmd[0].startswith("//"): continue
+ cmd = cmd[0]
+
+ 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
+ continue
+
+ if cmd == "udata":
+ m = re.match(r"^udata\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)
+ 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
+ continue
+
+ if cmd == "match":
+ block = dict()
+ block["type"] = "match"
+
+ line = line.split()
+ assert len(line) == 2
+ assert line[1] not in state_types
+ block["cell"] = line[1]
+ state_types[line[1]] = "Cell*";
+
+ block["if"] = list()
+ block["select"] = list()
+ block["index"] = list()
+ block["filter"] = list()
+ block["optional"] = False
+
+ while True:
+ l = f.readline()
+ assert l != ""
+ a = l.split()
+ if len(a) == 0 or a[0].startswith("//"): continue
+ if a[0] == "endmatch": break
+
+ if a[0] == "if":
+ b = l.lstrip()[2:]
+ block["if"].append(rewrite_cpp(b.strip()))
+ continue
+
+ if a[0] == "select":
+ b = l.lstrip()[6:]
+ block["select"].append(rewrite_cpp(b.strip()))
+ continue
+
+ if a[0] == "index":
+ m = re.match(r"^\s*index\s+<(.*?)>\s+(.*?)\s*===\s*(.*?)\s*$", l)
+ assert m
+ block["index"].append((m.group(1), rewrite_cpp(m.group(2)), rewrite_cpp(m.group(3))))
+ continue
+
+ if a[0] == "filter":
+ b = l.lstrip()[6:]
+ block["filter"].append(rewrite_cpp(b.strip()))
+ continue
+
+ if a[0] == "optional":
+ block["optional"] = True
+ continue
+
+ assert False
+
+ blocks.append(block)
+
+ if cmd == "code":
+ block = dict()
+ block["type"] = "code"
+ block["code"] = list()
+ block["states"] = set()
+
+ for s in line.split()[1:]:
+ assert s in state_types
+ block["states"].add(s)
+
+ while True:
+ l = f.readline()
+ assert l != ""
+ a = l.split()
+ if len(a) == 0: continue
+ if a[0] == "endcode": break
+
+ block["code"].append(rewrite_cpp(l.rstrip()))
+
+ blocks.append(block)
+
+with open(outfile, "w") as f:
+ print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f)
+ print("", file=f)
+
+ 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)
+ print(" std::function<void()> on_accept;".format(prefix), file=f)
+ print("", file=f)
+
+ for index in range(len(blocks)):
+ block = blocks[index]
+ if block["type"] == "match":
+ index_types = list()
+ for entry in block["index"]:
+ index_types.append(entry[0])
+ print(" typedef std::tuple<{}> index_{}_key_type;".format(", ".join(index_types), index), file=f)
+ print(" dict<index_{}_key_type, vector<Cell*>> index_{};".format(index, index), file=f)
+ print(" dict<SigBit, pool<Cell*>> sigusers;", file=f)
+ print(" pool<Cell*> blacklist_cells;", file=f)
+ print(" pool<Cell*> autoremove_cells;", file=f)
+ print(" bool blacklist_dirty;", file=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)
+
+ 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)
+
+ for v, n in sorted(ids.items()):
+ if n[0] == "\\":
+ print(" IdString {}{{\"\\{}\"}};".format(v, n), file=f)
+ else:
+ print(" IdString {}{{\"{}\"}};".format(v, n), file=f)
+ print("", file=f)
+
+ print(" void add_siguser(const SigSpec &sig, Cell *cell) {", file=f)
+ print(" for (auto bit : sigmap(sig)) {", file=f)
+ print(" if (bit.wire == nullptr) continue;", file=f)
+ print(" if (sigusers.count(bit) == 0 && bit.wire->port_id)", file=f)
+ print(" sigusers[bit].insert(nullptr);", file=f)
+ print(" sigusers[bit].insert(cell);", file=f)
+ print(" }", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" void blacklist(Cell *cell) {", file=f)
+ print(" if (cell != nullptr) {", file=f)
+ print(" if (blacklist_cells.insert(cell).second)", file=f)
+ print(" blacklist_dirty = true;", file=f)
+ print(" }", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" void autoremove(Cell *cell) {", file=f)
+ print(" if (cell != nullptr) {", file=f)
+ print(" if (blacklist_cells.insert(cell).second)", file=f)
+ print(" blacklist_dirty = true;", file=f)
+ print(" autoremove_cells.insert(cell);", file=f)
+ print(" }", file=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)
+
+ print(" SigSpec port(Cell *cell, IdString portname) {", file=f)
+ print(" return sigmap(cell->getPort(portname));", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" Const param(Cell *cell, IdString paramname) {", file=f)
+ print(" return cell->getParam(paramname);", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" int nusers(const SigSpec &sig) {", file=f)
+ print(" pool<Cell*> users;", file=f)
+ print(" for (auto bit : sigmap(sig))", file=f)
+ print(" for (auto user : sigusers[bit])", file=f)
+ print(" users.insert(user);", file=f)
+ print(" return GetSize(users);", file=f)
+ print(" }", file=f)
+ print("", file=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)
+ print(" for (auto cell : module->cells()) {", file=f)
+ print(" for (auto &conn : cell->connections())", file=f)
+ print(" add_siguser(conn.second, cell);", file=f)
+ print(" }", file=f)
+ print(" for (auto cell : cells) {", file=f)
+
+ for index in range(len(blocks)):
+ block = blocks[index]
+ if block["type"] == "match":
+ print(" do {", file=f)
+ print(" Cell *{} = cell;".format(block["cell"]), file=f)
+ for expr in block["select"]:
+ print(" if (!({})) break;".format(expr), file=f)
+ print(" index_{}_key_type key;".format(index), file=f)
+ for field, entry in enumerate(block["index"]):
+ print(" std::get<{}>(key) = {};".format(field, entry[1]), file=f)
+ print(" index_{}[key].push_back(cell);".format(index), file=f)
+ print(" } while (0);", file=f)
+
+ print(" }", file=f)
+ print(" }", file=f)
+ print("", file=f)
+
+ print(" ~{}_pm() {{".format(prefix), file=f)
+ print(" for (auto cell : autoremove_cells)", file=f)
+ print(" module->remove(cell);", file=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 index in range(len(blocks)):
+ block = blocks[index]
+
+ print(" void block_{}() {{".format(index), file=f)
+
+ const_st = set()
+ nonconst_st = set()
+ restore_st = set()
+
+ for i in range(index):
+ if blocks[i]["type"] == "code":
+ for s in blocks[i]["states"]:
+ const_st.add(s)
+ elif blocks[i]["type"] == "match":
+ const_st.add(blocks[i]["cell"])
+ else:
+ assert False
+
+ if block["type"] == "code":
+ for s in block["states"]:
+ if s in const_st:
+ const_st.remove(s)
+ restore_st.add(s)
+ nonconst_st.add(s)
+ elif block["type"] == "match":
+ s = block["cell"]
+ assert s not in const_st
+ nonconst_st.add(s)
+ else:
+ assert False
+
+ for s in sorted(const_st):
+ t = state_types[s]
+ if t.endswith("*"):
+ print(" {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+ else:
+ print(" const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+
+ for s in sorted(nonconst_st):
+ t = state_types[s]
+ print(" {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f)
+
+ if len(restore_st):
+ print("", file=f)
+ for s in sorted(restore_st):
+ t = state_types[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 branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
+
+ for line in block["code"]:
+ print(" " + line, file=f)
+
+ print("", file=f)
+ print(" block_{}();".format(index+1), file=f)
+ print("#undef reject", file=f)
+ print("#undef accept", file=f)
+ print("#undef branch", file=f)
+ print(" } while (0);", file=f)
+ print("", file=f)
+ print("rollback_label:", file=f)
+ print(" YS_ATTRIBUTE(unused);", file=f)
+
+ if len(restore_st) or len(nonconst_st):
+ print("", file=f)
+ for s in sorted(restore_st):
+ t = state_types[s]
+ print(" {} = backup_{};".format(s, s), file=f)
+ for s in sorted(nonconst_st):
+ if s not in restore_st:
+ t = state_types[s]
+ if t.endswith("*"):
+ print(" {} = nullptr;".format(s), file=f)
+ else:
+ print(" {} = {}();".format(s, t), file=f)
+
+ elif block["type"] == "match":
+ assert len(restore_st) == 0
+
+ if len(block["if"]):
+ for expr in block["if"]:
+ print("", file=f)
+ print(" if (!({})) {{".format(expr), file=f)
+ print(" {} = nullptr;".format(block["cell"]), file=f)
+ print(" block_{}();".format(index+1), file=f)
+ print(" return;", file=f)
+ print(" }", file=f)
+
+ print("", file=f)
+ print(" index_{}_key_type key;".format(index), file=f)
+ for field, entry in enumerate(block["index"]):
+ print(" std::get<{}>(key) = {};".format(field, entry[2]), file=f)
+ print(" const vector<Cell*> &cells = index_{}[key];".format(index), file=f)
+
+ print("", file=f)
+ print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f)
+ print(" {} = cells[idx];".format(block["cell"]), file=f)
+ print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f)
+ for expr in block["filter"]:
+ print(" if (!({})) continue;".format(expr), file=f)
+ print(" block_{}();".format(index+1), file=f)
+ print(" if (rollback) {", file=f)
+ print(" if (rollback != {}) {{".format(index+1), file=f)
+ print(" {} = nullptr;".format(block["cell"]), file=f)
+ print(" return;", file=f)
+ print(" }", file=f)
+ print(" rollback = 0;", file=f)
+ print(" }", file=f)
+ print(" }", file=f)
+
+ print("", file=f)
+ print(" {} = nullptr;".format(block["cell"]), file=f)
+
+ if block["optional"]:
+ print(" block_{}();".format(index+1), file=f)
+
+ else:
+ assert False
+
+
+ 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)
diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc
index b9e43d1d..52141a8e 100644
--- a/passes/proc/proc_clean.cc
+++ b/passes/proc/proc_clean.cc
@@ -77,18 +77,42 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did
}
else
{
- bool all_cases_are_empty = true;
- for (auto cs : sw->cases) {
- if (cs->actions.size() != 0 || cs->switches.size() != 0)
- all_cases_are_empty = false;
+ bool all_fully_def = true;
+ for (auto cs : sw->cases)
+ {
if (max_depth != 0)
proc_clean_case(cs, did_something, count, max_depth-1);
+ int size = 0;
+ for (auto cmp : cs->compare)
+ {
+ size += cmp.size();
+ if (!cmp.is_fully_def())
+ all_fully_def = false;
+ }
+ if (sw->signal.size() != size)
+ all_fully_def = false;
}
- if (all_cases_are_empty) {
- did_something = true;
- for (auto cs : sw->cases)
- delete cs;
- sw->cases.clear();
+ if (all_fully_def)
+ {
+ for (auto cs = sw->cases.begin(); cs != sw->cases.end();)
+ {
+ if ((*cs)->empty())
+ {
+ did_something = true;
+ delete *cs;
+ cs = sw->cases.erase(cs);
+ }
+ else ++cs;
+ }
+ }
+ else
+ {
+ while (!sw->cases.empty() && sw->cases.back()->empty())
+ {
+ did_something = true;
+ delete sw->cases.back();
+ sw->cases.pop_back();
+ }
}
}
}
@@ -106,7 +130,7 @@ void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int m
}
for (size_t i = 0; i < cs->switches.size(); i++) {
RTLIL::SwitchRule *sw = cs->switches[i];
- if (sw->cases.size() == 0) {
+ if (sw->empty()) {
cs->switches.erase(cs->switches.begin() + (i--));
did_something = true;
delete sw;
diff --git a/passes/sat/Makefile.inc b/passes/sat/Makefile.inc
index 8ab0280c..fc3ac879 100644
--- a/passes/sat/Makefile.inc
+++ b/passes/sat/Makefile.inc
@@ -8,4 +8,8 @@ OBJS += passes/sat/expose.o
OBJS += passes/sat/assertpmux.o
OBJS += passes/sat/clk2fflogic.o
OBJS += passes/sat/async2sync.o
+OBJS += passes/sat/supercover.o
+OBJS += passes/sat/fmcombine.o
+OBJS += passes/sat/mutate.o
+OBJS += passes/sat/cutpoint.o
diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc
index c92db711..d045d0dc 100644
--- a/passes/sat/async2sync.cc
+++ b/passes/sat/async2sync.cc
@@ -39,7 +39,7 @@ struct Async2syncPass : public Pass {
log("reset value in the next cycle regardless of the data-in value at the time of\n");
log("the clock edge.\n");
log("\n");
- log("Currently only $adff cells are supported by this pass.\n");
+ log("Currently only $adff and $dffsr cells are supported by this pass.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -84,7 +84,7 @@ struct Async2syncPass : public Pass {
bool arst_pol = cell->parameters["\\ARST_POLARITY"].as_bool();
Const arst_val = cell->parameters["\\ARST_VALUE"];
- SigSpec sig_clk = cell->getPort("\\CLK");
+ // SigSpec sig_clk = cell->getPort("\\CLK");
SigSpec sig_arst = cell->getPort("\\ARST");
SigSpec sig_d = cell->getPort("\\D");
SigSpec sig_q = cell->getPort("\\Q");
@@ -120,6 +120,55 @@ struct Async2syncPass : public Pass {
cell->type = "$dff";
continue;
}
+
+ if (cell->type.in("$dffsr"))
+ {
+ // bool clk_pol = cell->parameters["\\CLK_POLARITY"].as_bool();
+ bool set_pol = cell->parameters["\\SET_POLARITY"].as_bool();
+ bool clr_pol = cell->parameters["\\CLR_POLARITY"].as_bool();
+
+ // SigSpec sig_clk = cell->getPort("\\CLK");
+ SigSpec sig_set = cell->getPort("\\SET");
+ SigSpec sig_clr = cell->getPort("\\CLR");
+ SigSpec sig_d = cell->getPort("\\D");
+ SigSpec sig_q = cell->getPort("\\Q");
+
+ log("Replacing %s.%s (%s): SET=%s, CLR=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(sig_set), log_signal(sig_clr), log_signal(sig_d), log_signal(sig_q));
+
+ Const init_val;
+ for (int i = 0; i < GetSize(sig_q); i++) {
+ SigBit bit = sigmap(sig_q[i]);
+ init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx);
+ del_initbits.insert(bit);
+ }
+
+ Wire *new_d = module->addWire(NEW_ID, GetSize(sig_d));
+ Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q));
+ new_q->attributes["\\init"] = init_val;
+
+ if (!set_pol)
+ sig_set = module->Not(NEW_ID, sig_set);
+
+ if (clr_pol)
+ sig_clr = module->Not(NEW_ID, sig_clr);
+
+ SigSpec tmp = module->Or(NEW_ID, sig_d, sig_set);
+ module->addAnd(NEW_ID, tmp, sig_clr, new_d);
+
+ tmp = module->Or(NEW_ID, new_q, sig_set);
+ module->addAnd(NEW_ID, tmp, sig_clr, sig_q);
+
+ cell->setPort("\\D", new_d);
+ cell->setPort("\\Q", new_q);
+ cell->unsetPort("\\SET");
+ cell->unsetPort("\\CLR");
+ cell->unsetParam("\\SET_POLARITY");
+ cell->unsetParam("\\CLR_POLARITY");
+ cell->type = "$dff";
+ continue;
+ }
}
for (auto wire : module->wires())
diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc
new file mode 100644
index 00000000..048aec7f
--- /dev/null
+++ b/passes/sat/cutpoint.cc
@@ -0,0 +1,168 @@
+/*
+ * 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 CutpointPass : public Pass {
+ CutpointPass() : Pass("cutpoint", "add hi/lo cover cells for each wire bit") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" cutpoint [options] [selection]\n");
+ log("\n");
+ log("This command adds formal cut points to the design.\n");
+ log("\n");
+ log(" -undef\n");
+ log(" set cupoint nets to undef (x). the default behavior is to create a\n");
+ log(" $anyseq cell and drive the cutpoint net from that\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ bool flag_undef = false;
+
+ log_header(design, "Executing CUTPOINT pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-undef") {
+ flag_undef = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ if (design->selected_whole_module(module->name)) {
+ log("Making all outputs of module %s cut points, removing module contents.\n", log_id(module));
+ module->new_connections(std::vector<RTLIL::SigSig>());
+ for (auto cell : vector<Cell*>(module->cells()))
+ module->remove(cell);
+ vector<Wire*> output_wires;
+ for (auto wire : module->wires())
+ if (wire->port_output)
+ output_wires.push_back(wire);
+ for (auto wire : output_wires)
+ module->connect(wire, flag_undef ? Const(State::Sx, GetSize(wire)) : module->Anyseq(NEW_ID, GetSize(wire)));
+ continue;
+ }
+
+ SigMap sigmap(module);
+ pool<SigBit> cutpoint_bits;
+
+ for (auto cell : module->selected_cells()) {
+ if (cell->type == "$anyseq")
+ continue;
+ log("Removing cell %s.%s, making all cell outputs cutpoints.\n", log_id(module), log_id(cell));
+ for (auto &conn : cell->connections()) {
+ if (cell->output(conn.first))
+ module->connect(conn.second, flag_undef ? Const(State::Sx, GetSize(conn.second)) : module->Anyseq(NEW_ID, GetSize(conn.second)));
+ }
+ module->remove(cell);
+ }
+
+ for (auto wire : module->selected_wires()) {
+ if (wire->port_output) {
+ log("Making output wire %s.%s a cutpoint.\n", log_id(module), log_id(wire));
+ Wire *new_wire = module->addWire(NEW_ID, wire);
+ module->swap_names(wire, new_wire);
+ module->connect(new_wire, flag_undef ? Const(State::Sx, GetSize(new_wire)) : module->Anyseq(NEW_ID, GetSize(new_wire)));
+ wire->port_id = 0;
+ wire->port_input = false;
+ wire->port_output = false;
+ continue;
+ }
+ log("Making wire %s.%s a cutpoint.\n", log_id(module), log_id(wire));
+ for (auto bit : sigmap(wire))
+ cutpoint_bits.insert(bit);
+ }
+
+ if (!cutpoint_bits.empty())
+ {
+ for (auto cell : module->cells()) {
+ for (auto &conn : cell->connections()) {
+ if (!cell->output(conn.first))
+ continue;
+ SigSpec sig = sigmap(conn.second);
+ int bit_count = 0;
+ for (auto &bit : sig) {
+ if (cutpoint_bits.count(bit))
+ bit_count++;
+ }
+ if (bit_count == 0)
+ continue;
+ SigSpec dummy = module->addWire(NEW_ID, bit_count);
+ bit_count = 0;
+ for (auto &bit : sig) {
+ if (cutpoint_bits.count(bit))
+ bit = dummy[bit_count++];
+ }
+ cell->setPort(conn.first, sig);
+ }
+ }
+
+ vector<Wire*> rewrite_wires;
+ for (auto wire : module->wires()) {
+ if (!wire->port_input)
+ continue;
+ int bit_count = 0;
+ for (auto &bit : sigmap(wire))
+ if (cutpoint_bits.count(bit))
+ bit_count++;
+ if (bit_count)
+ rewrite_wires.push_back(wire);
+ }
+
+ for (auto wire : rewrite_wires) {
+ Wire *new_wire = module->addWire(NEW_ID, wire);
+ SigSpec lhs, rhs, sig = sigmap(wire);
+ for (int i = 0; i < GetSize(sig); i++)
+ if (!cutpoint_bits.count(sig[i])) {
+ lhs.append(SigBit(wire, i));
+ rhs.append(SigBit(new_wire, i));
+ }
+ if (GetSize(lhs))
+ module->connect(lhs, rhs);
+ module->swap_names(wire, new_wire);
+ wire->port_id = 0;
+ wire->port_input = false;
+ wire->port_output = false;
+ }
+
+ SigSpec sig(cutpoint_bits);
+ sig.sort_and_unify();
+
+ for (auto chunk : sig.chunks()) {
+ SigSpec s(chunk);
+ module->connect(s, flag_undef ? Const(State::Sx, GetSize(s)) : module->Anyseq(NEW_ID, GetSize(s)));
+ }
+ }
+ }
+ }
+} CutpointPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/fmcombine.cc b/passes/sat/fmcombine.cc
new file mode 100644
index 00000000..cd75ca86
--- /dev/null
+++ b/passes/sat/fmcombine.cc
@@ -0,0 +1,341 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct opts_t
+{
+ bool fwd = false;
+ bool bwd = false;
+ bool nop = false;
+};
+
+struct FmcombineWorker
+{
+ const opts_t &opts;
+ Design *design;
+ Module *original = nullptr;
+ Module *module = nullptr;
+ IdString orig_type, combined_type;
+
+ FmcombineWorker(Design *design, IdString orig_type, const opts_t &opts) :
+ opts(opts), design(design), original(design->module(orig_type)),
+ orig_type(orig_type), combined_type("$fmcombine" + orig_type.str())
+ {
+ }
+
+ SigSpec import_sig(SigSpec sig, const string &suffix)
+ {
+ SigSpec newsig;
+ for (auto chunk : sig.chunks()) {
+ if (chunk.wire != nullptr)
+ chunk.wire = module->wire(chunk.wire->name.str() + suffix);
+ newsig.append(chunk);
+ }
+ return newsig;
+ }
+
+ void import_prim_cell(Cell *cell, const string &suffix)
+ {
+ Cell *c = module->addCell(cell->name.str() + suffix, cell->type);
+ c->parameters = cell->parameters;
+ c->attributes = cell->attributes;
+
+ for (auto &conn : cell->connections())
+ c->setPort(conn.first, import_sig(conn.second, suffix));
+ }
+
+ void import_hier_cell(Cell *cell)
+ {
+ if (!cell->parameters.empty())
+ log_cmd_error("Cell %s.%s has unresolved instance parameters.\n", log_id(original), log_id(cell));
+
+ FmcombineWorker sub_worker(design, cell->type, opts);
+ sub_worker.generate();
+
+ Cell *c = module->addCell(cell->name.str() + "_combined", sub_worker.combined_type);
+ // c->parameters = cell->parameters;
+ c->attributes = cell->attributes;
+
+ for (auto &conn : cell->connections()) {
+ c->setPort(conn.first.str() + "_gold", import_sig(conn.second, "_gold"));
+ c->setPort(conn.first.str() + "_gate", import_sig(conn.second, "_gate"));
+ }
+ }
+
+ void generate()
+ {
+ if (design->module(combined_type)) {
+ // log("Combined module %s already exists.\n", log_id(combined_type));
+ return;
+ }
+
+ log("Generating combined module %s from module %s.\n", log_id(combined_type), log_id(orig_type));
+ module = design->addModule(combined_type);
+
+ for (auto wire : original->wires()) {
+ module->addWire(wire->name.str() + "_gold", wire);
+ module->addWire(wire->name.str() + "_gate", wire);
+ }
+ module->fixup_ports();
+
+ for (auto cell : original->cells()) {
+ if (design->module(cell->type) == nullptr) {
+ import_prim_cell(cell, "_gold");
+ import_prim_cell(cell, "_gate");
+ } else {
+ import_hier_cell(cell);
+ }
+ }
+
+ for (auto &conn : original->connections()) {
+ module->connect(import_sig(conn.first, "_gold"), import_sig(conn.second, "_gold"));
+ module->connect(import_sig(conn.first, "_gate"), import_sig(conn.second, "_gate"));
+ }
+
+ if (opts.nop)
+ return;
+
+ CellTypes ct;
+ ct.setup_internals_eval();
+ ct.setup_stdcells_eval();
+
+ SigMap sigmap(module);
+
+ dict<SigBit, SigBit> data_bit_to_eq_net;
+ dict<Cell*, SigSpec> cell_to_eq_nets;
+ dict<SigSpec, SigSpec> reduce_db;
+ dict<SigSpec, SigSpec> invert_db;
+
+ for (auto cell : original->cells())
+ {
+ if (!ct.cell_known(cell->type))
+ continue;
+
+ for (auto &conn : cell->connections())
+ {
+ if (!cell->output(conn.first))
+ continue;
+
+ SigSpec A = import_sig(conn.second, "_gold");
+ SigSpec B = import_sig(conn.second, "_gate");
+ SigBit EQ = module->Eq(NEW_ID, A, B);
+
+ for (auto bit : sigmap({A, B}))
+ data_bit_to_eq_net[bit] = EQ;
+
+ cell_to_eq_nets[cell].append(EQ);
+ }
+ }
+
+ for (auto cell : original->cells())
+ {
+ if (!ct.cell_known(cell->type))
+ continue;
+
+ bool skip_cell = !cell_to_eq_nets.count(cell);
+ pool<SigBit> src_eq_bits;
+
+ for (auto &conn : cell->connections())
+ {
+ if (skip_cell)
+ break;
+
+ if (cell->output(conn.first))
+ continue;
+
+ SigSpec A = import_sig(conn.second, "_gold");
+ SigSpec B = import_sig(conn.second, "_gate");
+
+ for (auto bit : sigmap({A, B})) {
+ if (data_bit_to_eq_net.count(bit))
+ src_eq_bits.insert(data_bit_to_eq_net.at(bit));
+ else
+ skip_cell = true;
+ }
+ }
+
+ if (!skip_cell) {
+ SigSpec antecedent = SigSpec(src_eq_bits);
+ antecedent.sort_and_unify();
+
+ if (GetSize(antecedent) > 1) {
+ if (reduce_db.count(antecedent) == 0)
+ reduce_db[antecedent] = module->ReduceAnd(NEW_ID, antecedent);
+ antecedent = reduce_db.at(antecedent);
+ }
+
+ SigSpec consequent = cell_to_eq_nets.at(cell);
+ consequent.sort_and_unify();
+
+ if (GetSize(consequent) > 1) {
+ if (reduce_db.count(consequent) == 0)
+ reduce_db[consequent] = module->ReduceAnd(NEW_ID, consequent);
+ consequent = reduce_db.at(consequent);
+ }
+
+ if (opts.fwd)
+ module->addAssume(NEW_ID, consequent, antecedent);
+
+ if (opts.bwd)
+ {
+ if (invert_db.count(antecedent) == 0)
+ invert_db[antecedent] = module->Not(NEW_ID, antecedent);
+
+ if (invert_db.count(consequent) == 0)
+ invert_db[consequent] = module->Not(NEW_ID, consequent);
+
+ module->addAssume(NEW_ID, invert_db.at(antecedent), invert_db.at(consequent));
+ }
+ }
+ }
+ }
+};
+
+struct FmcombinePass : public Pass {
+ FmcombinePass() : Pass("fmcombine", "combine two instances of a cell into one") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" fmcombine [options] module_name gold_cell gate_cell\n");
+ // log(" fmcombine [options] @gold_cell @gate_cell\n");
+ log("\n");
+ log("This pass takes two cells, which are instances of the same module, and replaces\n");
+ log("them with one instance of a special 'combined' module, that effectively\n");
+ log("contains two copies of the original module, plus some formal properties.\n");
+ log("\n");
+ 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(" -fwd\n");
+ log(" Insert forward hint assumptions into the combined module.\n");
+ log("\n");
+ log(" -bwd\n");
+ log(" Insert backward hint assumptions into the combined module.\n");
+ log(" (Backward hints are logically equivalend to fordward hits, but\n");
+ log(" some solvers are faster with bwd hints, or even both -bwd and -fwd.)\n");
+ log("\n");
+ log(" -nop\n");
+ log(" Don't insert hint assumptions into the combined module.\n");
+ log(" (This should not provide any speedup over the original design, but\n");
+ log(" strangely sometimes it does.)\n");
+ log("\n");
+ log("If none of -fwd, -bwd, and -nop is given, then -fwd is used as default.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ opts_t opts;
+ Module *module = nullptr;
+ Cell *gold_cell = nullptr;
+ Cell *gate_cell = nullptr;
+
+ log_header(design, "Executing FMCOMBINE pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-o" && argidx+1 < args.size()) {
+ // filename = args[++argidx];
+ // continue;
+ // }
+ if (args[argidx] == "-fwd") {
+ opts.fwd = true;
+ continue;
+ }
+ if (args[argidx] == "-bwd") {
+ opts.bwd = true;
+ continue;
+ }
+ if (args[argidx] == "-nop") {
+ opts.nop = true;
+ continue;
+ }
+ break;
+ }
+ if (argidx+2 == args.size())
+ {
+ string gold_name = args[argidx++];
+ string gate_name = args[argidx++];
+ log_cmd_error("fmcombine @gold_cell @gate_cell call style is not implemented yet.");
+ }
+ else if (argidx+3 == args.size())
+ {
+ IdString module_name = RTLIL::escape_id(args[argidx++]);
+ IdString gold_name = RTLIL::escape_id(args[argidx++]);
+ IdString gate_name = RTLIL::escape_id(args[argidx++]);
+
+ module = design->module(module_name);
+ if (module == nullptr)
+ log_cmd_error("Module %s not found.\n", log_id(module_name));
+
+ gold_cell = module->cell(gold_name);
+ if (gold_cell == nullptr)
+ log_cmd_error("Gold cell %s not found in module %s.\n", log_id(gold_name), log_id(module));
+
+ 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));
+ }
+ else
+ {
+ log_cmd_error("Invalid number of arguments.\n");
+ }
+ // extra_args(args, argidx, design);
+
+ if (opts.nop && (opts.fwd || opts.bwd))
+ log_cmd_error("Option -nop can not be combined with -fwd and/or -bwd.\n");
+
+ if (!opts.nop && !opts.fwd && !opts.bwd)
+ opts.fwd = true;
+
+ if (gold_cell->type != gate_cell->type)
+ log_cmd_error("Types of gold and gate cells do not match.\n");
+ 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");
+
+ FmcombineWorker worker(design, gold_cell->type, opts);
+ worker.generate();
+ IdString combined_cell_name = module->uniquify(stringf("\\%s_%s", log_id(gold_cell), log_id(gate_cell)));
+
+ Cell *cell = module->addCell(combined_cell_name, worker.combined_type);
+ cell->attributes = gold_cell->attributes;
+ cell->add_strpool_attribute("\\src", gate_cell->get_strpool_attribute("\\src"));
+
+ log("Combining cells %s and %s in module %s into new cell %s.\n", log_id(gold_cell), log_id(gate_cell), log_id(module), log_id(cell));
+
+ for (auto &conn : gold_cell->connections())
+ cell->setPort(conn.first.str() + "_gold", conn.second);
+ module->remove(gold_cell);
+
+ for (auto &conn : gate_cell->connections())
+ cell->setPort(conn.first.str() + "_gate", conn.second);
+ module->remove(gate_cell);
+ }
+} FmcombinePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/mutate.cc b/passes/sat/mutate.cc
new file mode 100644
index 00000000..c50678c5
--- /dev/null
+++ b/passes/sat/mutate.cc
@@ -0,0 +1,956 @@
+/*
+ * 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 mutate_t {
+ string mode;
+ pool<string> src;
+ IdString module, cell;
+ IdString port, wire;
+ int portbit = -1;
+ int ctrlbit = -1;
+ int wirebit = -1;
+ bool used = false;
+};
+
+struct mutate_opts_t {
+ int seed = 0;
+ std::string mode;
+ pool<string> src;
+ IdString module, cell, port, wire;
+ int portbit = -1;
+ int ctrlbit = -1;
+ int wirebit = -1;
+
+ IdString ctrl_name;
+ int ctrl_width = -1, ctrl_value = -1;
+
+ bool none = false;
+
+ int pick_cover_prcnt = 80;
+
+ int weight_cover = 500;
+
+ int weight_pq_w = 100;
+ int weight_pq_b = 100;
+ int weight_pq_c = 100;
+ int weight_pq_s = 100;
+
+ int weight_pq_mw = 100;
+ int weight_pq_mb = 100;
+ int weight_pq_mc = 100;
+ int weight_pq_ms = 100;
+};
+
+void database_add(std::vector<mutate_t> &database, const mutate_opts_t &opts, const mutate_t &entry)
+{
+ if (!opts.mode.empty() && opts.mode != entry.mode)
+ return;
+
+ if (!opts.src.empty()) {
+ bool found_match = false;
+ for (auto &s : opts.src) {
+ if (entry.src.count(s))
+ found_match = true;
+ }
+ if (!found_match)
+ return;
+ }
+
+ if (!opts.module.empty() && opts.module != entry.module)
+ return;
+
+ if (!opts.cell.empty() && opts.cell != entry.cell)
+ return;
+
+ if (!opts.port.empty() && opts.port != entry.port)
+ return;
+
+ if (opts.portbit >= 0 && opts.portbit != entry.portbit)
+ return;
+
+ if (opts.ctrlbit >= 0 && opts.ctrlbit != entry.ctrlbit)
+ return;
+
+ if (!opts.wire.empty() && opts.wire != entry.wire)
+ return;
+
+ if (opts.wirebit >= 0 && opts.wirebit != entry.wirebit)
+ return;
+
+ database.push_back(entry);
+}
+
+struct xs128_t
+{
+ uint32_t x = 123456789;
+ uint32_t y = 0, z = 0, w = 0;
+
+ xs128_t(int seed = 0) : w(seed) {
+ next();
+ next();
+ next();
+ }
+
+ void next() {
+ uint32_t t = x ^ (x << 11);
+ x = y, y = z, z = w;
+ w ^= (w >> 19) ^ t ^ (t >> 8);
+ }
+
+ int operator()() {
+ next();
+ return w & 0x3fffffff;
+ }
+
+ int operator()(int n) {
+ if (n < 2)
+ return 0;
+ while (1) {
+ int k = (*this)(), p = k % n;
+ if ((k - p + n) <= 0x40000000)
+ return p;
+ }
+ }
+};
+
+struct coverdb_t
+{
+ dict<string, int> src_db;
+ dict<tuple<IdString, IdString>, int> wire_db;
+ dict<tuple<IdString, IdString, int>, int> wirebit_db;
+
+ void insert(const mutate_t &m) {
+ if (!m.wire.empty()) {
+ wire_db[tuple<IdString, IdString>(m.module, m.wire)] = 0;
+ wirebit_db[tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit)] = 0;
+ }
+ for (auto &s : m.src) {
+ src_db[s] = 0;
+ }
+ }
+
+ void update(const mutate_t &m) {
+ if (!m.wire.empty()) {
+ wire_db.at(tuple<IdString, IdString>(m.module, m.wire))++;
+ wirebit_db.at(tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit))++;
+ }
+ for (auto &s : m.src) {
+ src_db.at(s)++;
+ }
+ }
+
+ int score(const mutate_t &m) {
+ int this_score = m.src.empty() ? 0 : 1;
+ if (!m.wire.empty()) {
+ this_score += wire_db.at(tuple<IdString, IdString>(m.module, m.wire)) ? 0 : 5;
+ this_score += wirebit_db.at(tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit)) ? 0 : 1;
+ }
+ for (auto &s : m.src) {
+ this_score += src_db.at(s) ? 0 : 5;
+ }
+ return this_score;
+ }
+};
+
+struct mutate_queue_t
+{
+ pool<mutate_t*, hash_ptr_ops> db;
+
+ mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) {
+ mutate_t *m = nullptr;
+ if (rng(100) < opts.pick_cover_prcnt) {
+ vector<mutate_t*> candidates, rmqueue;
+ int best_score = -1;
+ for (auto p : db) {
+ if (p->used) {
+ rmqueue.push_back(p);
+ continue;
+ }
+ int this_score = coverdb.score(*p);
+ if (this_score > best_score) {
+ best_score = this_score;
+ candidates.clear();
+ }
+ if (best_score == this_score)
+ candidates.push_back(p);
+ }
+ for (auto p : rmqueue)
+ db.erase(p);
+ if (!candidates.empty())
+ m = candidates[rng(GetSize(candidates))];
+ }
+ if (m == nullptr) {
+ while (!db.empty()) {
+ int i = rng(GetSize(db));
+ auto it = db.element(i);
+ mutate_t *p = *it;
+ db.erase(it);
+ if (p->used == false) {
+ m = p;
+ break;
+ }
+ }
+ }
+ return m;
+ }
+
+ void add(mutate_t *m) {
+ db.insert(m);
+ }
+};
+
+template <typename K, typename T>
+struct mutate_chain_queue_t
+{
+ dict<K, T> db;
+
+ mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) {
+ while (!db.empty()) {
+ int i = rng(GetSize(db));
+ auto it = db.element(i);
+ mutate_t *m = it->second.pick(rng, coverdb, opts);
+ if (m != nullptr)
+ return m;
+ db.erase(it);
+ }
+ return nullptr;
+ }
+
+ template<typename... Args>
+ void add(mutate_t *m, K key, Args... args) {
+ db[key].add(m, args...);
+ }
+};
+
+template <typename K, typename T>
+struct mutate_once_queue_t
+{
+ dict<K, T> db;
+
+ mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) {
+ while (!db.empty()) {
+ int i = rng(GetSize(db));
+ auto it = db.element(i);
+ mutate_t *m = it->second.pick(rng, coverdb, opts);
+ db.erase(it);
+ if (m != nullptr)
+ return m;
+ }
+ return nullptr;
+ }
+
+ template<typename... Args>
+ void add(mutate_t *m, K key, Args... args) {
+ db[key].add(m, args...);
+ }
+};
+
+void database_reduce(std::vector<mutate_t> &database, const mutate_opts_t &opts, int N, xs128_t &rng)
+{
+ std::vector<mutate_t> new_database;
+ coverdb_t coverdb;
+
+ int total_weight = opts.weight_cover + opts.weight_pq_w + opts.weight_pq_b + opts.weight_pq_c + opts.weight_pq_s;
+ total_weight += opts.weight_pq_mw + opts.weight_pq_mb + opts.weight_pq_mc + opts.weight_pq_ms;
+
+ if (N >= GetSize(database))
+ return;
+
+ mutate_once_queue_t<tuple<IdString, IdString>, mutate_queue_t> primary_queue_wire;
+ mutate_once_queue_t<tuple<IdString, IdString, int>, mutate_queue_t> primary_queue_bit;
+ mutate_once_queue_t<tuple<IdString, IdString>, mutate_queue_t> primary_queue_cell;
+ mutate_once_queue_t<string, mutate_queue_t> primary_queue_src;
+
+ mutate_chain_queue_t<IdString, mutate_once_queue_t<IdString, mutate_queue_t>> primary_queue_module_wire;
+ mutate_chain_queue_t<IdString, mutate_once_queue_t<pair<IdString, int>, mutate_queue_t>> primary_queue_module_bit;
+ mutate_chain_queue_t<IdString, mutate_once_queue_t<IdString, mutate_queue_t>> primary_queue_module_cell;
+ mutate_chain_queue_t<IdString, mutate_once_queue_t<string, mutate_queue_t>> primary_queue_module_src;
+
+ for (auto &m : database)
+ {
+ coverdb.insert(m);
+
+ if (!m.wire.empty()) {
+ primary_queue_wire.add(&m, tuple<IdString, IdString>(m.module, m.wire));
+ primary_queue_bit.add(&m, tuple<IdString, IdString, int>(m.module, m.wire, m.wirebit));
+ primary_queue_module_wire.add(&m, m.module, m.wire);
+ primary_queue_module_bit.add(&m, m.module, pair<IdString, int>(m.wire, m.wirebit));
+ }
+
+ primary_queue_cell.add(&m, tuple<IdString, IdString>(m.module, m.cell));
+ primary_queue_module_cell.add(&m, m.module, m.cell);
+
+ for (auto &s : m.src) {
+ primary_queue_src.add(&m, s);
+ primary_queue_module_src.add(&m, m.module, s);
+ }
+ }
+
+ vector<mutate_t*> cover_candidates;
+ int best_cover_score = -1;
+ bool skip_cover = false;
+
+ while (GetSize(new_database) < N)
+ {
+ int k = rng(total_weight);
+
+ k -= opts.weight_cover;
+ if (k < 0) {
+ while (!skip_cover) {
+ if (cover_candidates.empty()) {
+ best_cover_score = -1;
+ for (auto &m : database) {
+ if (m.used || m.src.empty())
+ continue;
+ int this_score = -1;
+ for (auto &s : m.src) {
+ if (this_score == -1 || this_score > coverdb.src_db.at(s))
+ this_score = coverdb.src_db.at(s);
+ }
+ log_assert(this_score != -1);
+ if (best_cover_score == -1 || this_score < best_cover_score) {
+ cover_candidates.clear();
+ best_cover_score = this_score;
+ }
+ if (best_cover_score == this_score)
+ cover_candidates.push_back(&m);
+ }
+ if (best_cover_score == -1) {
+ skip_cover = true;
+ break;
+ }
+ }
+
+ mutate_t *m = nullptr;
+ while (!cover_candidates.empty())
+ {
+ int idx = rng(GetSize(cover_candidates));
+ mutate_t *p = cover_candidates[idx];
+ cover_candidates[idx] = cover_candidates.back();
+ cover_candidates.pop_back();
+
+ if (p->used)
+ continue;
+
+ int this_score = -1;
+ for (auto &s : p->src) {
+ if (this_score == -1 || this_score > coverdb.src_db.at(s))
+ this_score = coverdb.src_db.at(s);
+ }
+
+ if (this_score != best_cover_score)
+ continue;
+
+ m = p;
+ break;
+ }
+
+ if (m != nullptr) {
+ m->used = true;
+ coverdb.update(*m);
+ new_database.push_back(*m);
+ break;
+ }
+ }
+ continue;
+ }
+
+#define X(__wght, __queue) \
+ k -= __wght; \
+ if (k < 0) { \
+ mutate_t *m = __queue.pick(rng, coverdb, opts); \
+ if (m != nullptr) { \
+ m->used = true; \
+ coverdb.update(*m); \
+ new_database.push_back(*m); \
+ }; \
+ continue; \
+ }
+
+ X(opts.weight_pq_w, primary_queue_wire)
+ X(opts.weight_pq_b, primary_queue_bit)
+ X(opts.weight_pq_c, primary_queue_cell)
+ X(opts.weight_pq_s, primary_queue_src)
+
+ X(opts.weight_pq_mw, primary_queue_module_wire)
+ X(opts.weight_pq_mb, primary_queue_module_bit)
+ X(opts.weight_pq_mc, primary_queue_module_cell)
+ X(opts.weight_pq_ms, primary_queue_module_src)
+#undef X
+ }
+
+ std::swap(new_database, database);
+
+ int covered_src_cnt = 0;
+ int covered_wire_cnt = 0;
+ int covered_wirebit_cnt = 0;
+
+ for (auto &it : coverdb.src_db)
+ if (it.second)
+ covered_src_cnt++;
+
+ for (auto &it : coverdb.wire_db)
+ if (it.second)
+ covered_wire_cnt++;
+
+ for (auto &it : coverdb.wirebit_db)
+ if (it.second)
+ covered_wirebit_cnt++;
+
+ log("Covered %d/%d src attributes (%.2f%%).\n", covered_src_cnt, GetSize(coverdb.src_db), 100.0 * covered_src_cnt / GetSize(coverdb.src_db));
+ log("Covered %d/%d wires (%.2f%%).\n", covered_wire_cnt, GetSize(coverdb.wire_db), 100.0 * covered_wire_cnt / GetSize(coverdb.wire_db));
+ log("Covered %d/%d wire bits (%.2f%%).\n", covered_wirebit_cnt, GetSize(coverdb.wirebit_db), 100.0 * covered_wirebit_cnt / GetSize(coverdb.wirebit_db));
+}
+
+void mutate_list(Design *design, const mutate_opts_t &opts, const string &filename, const string &srcsfile, int N)
+{
+ pool<string> sources;
+ std::vector<mutate_t> database;
+ xs128_t rng(opts.seed);
+
+ for (auto module : design->selected_modules())
+ {
+ if (!opts.module.empty() && module->name != opts.module)
+ continue;
+
+ SigMap sigmap(module);
+ dict<SigBit, int> bit_user_cnt;
+
+ for (auto wire : module->wires()) {
+ if (wire->name[0] == '\\' && wire->attributes.count("\\src"))
+ sigmap.add(wire);
+ }
+
+ for (auto cell : module->cells()) {
+ for (auto &conn : cell->connections()) {
+ if (cell->output(conn.first))
+ continue;
+ for (auto bit : sigmap(conn.second))
+ bit_user_cnt[bit]++;
+ }
+ }
+
+ for (auto wire : module->selected_wires())
+ {
+ for (SigBit bit : SigSpec(wire))
+ {
+ SigBit sigbit = sigmap(bit);
+
+ if (bit.wire == nullptr || sigbit.wire == nullptr)
+ continue;
+
+ if (!bit.wire->port_id != !sigbit.wire->port_id) {
+ if (bit.wire->port_id)
+ sigmap.add(bit);
+ continue;
+ }
+
+ if (!bit.wire->name[0] != !sigbit.wire->name[0]) {
+ if (bit.wire->name[0] == '\\')
+ sigmap.add(bit);
+ continue;
+ }
+ }
+ }
+
+ for (auto cell : module->selected_cells())
+ {
+ if (!opts.cell.empty() && cell->name != opts.cell)
+ continue;
+
+ for (auto &conn : cell->connections())
+ {
+ for (int i = 0; i < GetSize(conn.second); i++) {
+ mutate_t entry;
+ entry.module = module->name;
+ entry.cell = cell->name;
+ entry.port = conn.first;
+ entry.portbit = i;
+
+ for (auto &s : cell->get_strpool_attribute("\\src"))
+ entry.src.insert(s);
+
+ SigBit bit = sigmap(conn.second[i]);
+ if (bit.wire && bit.wire->name[0] == '\\' && (cell->output(conn.first) || bit_user_cnt[bit] == 1)) {
+ for (auto &s : bit.wire->get_strpool_attribute("\\src"))
+ entry.src.insert(s);
+ entry.wire = bit.wire->name;
+ entry.wirebit = bit.offset;
+ }
+
+ if (!srcsfile.empty())
+ sources.insert(entry.src.begin(), entry.src.end());
+
+ entry.mode = "inv";
+ database_add(database, opts, entry);
+
+ entry.mode = "const0";
+ database_add(database, opts, entry);
+
+ entry.mode = "const1";
+ database_add(database, opts, entry);
+
+ entry.mode = "cnot0";
+ entry.ctrlbit = rng(GetSize(conn.second));
+ if (entry.ctrlbit != entry.portbit && conn.second[entry.ctrlbit].wire)
+ database_add(database, opts, entry);
+
+ entry.mode = "cnot1";
+ entry.ctrlbit = rng(GetSize(conn.second));
+ if (entry.ctrlbit != entry.portbit && conn.second[entry.ctrlbit].wire)
+ database_add(database, opts, entry);
+ }
+ }
+ }
+ }
+
+ log("Raw database size: %d\n", GetSize(database));
+ if (N != 0) {
+ database_reduce(database, opts, opts.none ? N-1 : N, rng);
+ log("Reduced database size: %d\n", GetSize(database));
+ }
+
+ if (!srcsfile.empty()) {
+ std::ofstream sout;
+ sout.open(srcsfile, std::ios::out | std::ios::trunc);
+ if (!sout.is_open())
+ log_error("Could not open file \"%s\" with write access.\n", srcsfile.c_str());
+ sources.sort();
+ for (auto &s : sources)
+ sout << s << std::endl;
+ }
+
+ std::ofstream fout;
+
+ if (!filename.empty()) {
+ fout.open(filename, std::ios::out | std::ios::trunc);
+ if (!fout.is_open())
+ log_error("Could not open file \"%s\" with write access.\n", filename.c_str());
+ }
+
+ int ctrl_value = opts.ctrl_value;
+
+ if (opts.none) {
+ string str = "mutate";
+ if (!opts.ctrl_name.empty())
+ str += stringf(" -ctrl %s %d %d", log_id(opts.ctrl_name), opts.ctrl_width, ctrl_value++);
+ str += " -mode none";
+ if (filename.empty())
+ log("%s\n", str.c_str());
+ else
+ fout << str << std::endl;
+ }
+
+ for (auto &entry : database) {
+ string str = "mutate";
+ if (!opts.ctrl_name.empty())
+ str += stringf(" -ctrl %s %d %d", log_id(opts.ctrl_name), opts.ctrl_width, ctrl_value++);
+ str += stringf(" -mode %s", entry.mode.c_str());
+ if (!entry.module.empty())
+ str += stringf(" -module %s", log_id(entry.module));
+ if (!entry.cell.empty())
+ str += stringf(" -cell %s", log_id(entry.cell));
+ if (!entry.port.empty())
+ str += stringf(" -port %s", log_id(entry.port));
+ if (entry.portbit >= 0)
+ str += stringf(" -portbit %d", entry.portbit);
+ if (entry.ctrlbit >= 0)
+ str += stringf(" -ctrlbit %d", entry.ctrlbit);
+ if (!entry.wire.empty())
+ str += stringf(" -wire %s", log_id(entry.wire));
+ if (entry.wirebit >= 0)
+ str += stringf(" -wirebit %d", entry.wirebit);
+ for (auto &s : entry.src)
+ str += stringf(" -src %s", s.c_str());
+ if (filename.empty())
+ log("%s\n", str.c_str());
+ else
+ fout << str << std::endl;
+ }
+}
+
+SigSpec mutate_ctrl_sig(Module *module, IdString name, int width)
+{
+ Wire *ctrl_wire = module->wire(name);
+
+ if (ctrl_wire == nullptr)
+ {
+ log("Adding ctrl port %s to module %s.\n", log_id(name), log_id(module));
+
+ ctrl_wire = module->addWire(name, width);
+ ctrl_wire->port_input = true;
+ module->fixup_ports();
+
+ for (auto mod : module->design->modules())
+ for (auto cell : mod->cells())
+ {
+ if (cell->type != module->name)
+ continue;
+
+ SigSpec ctrl = mutate_ctrl_sig(mod, name, width);
+
+ log("Connecting ctrl port to cell %s in module %s.\n", log_id(cell), log_id(mod));
+ cell->setPort(name, ctrl);
+ }
+ }
+
+ log_assert(GetSize(ctrl_wire) == width);
+ return ctrl_wire;
+}
+
+SigBit mutate_ctrl(Module *module, const mutate_opts_t &opts)
+{
+ if (opts.ctrl_name.empty())
+ return State::S1;
+
+ SigSpec sig = mutate_ctrl_sig(module, opts.ctrl_name, opts.ctrl_width);
+ return module->Eq(NEW_ID, sig, Const(opts.ctrl_value, GetSize(sig)));
+}
+
+SigSpec mutate_ctrl_mux(Module *module, const mutate_opts_t &opts, SigSpec unchanged_sig, SigSpec changed_sig)
+{
+ SigBit ctrl_bit = mutate_ctrl(module, opts);
+ if (ctrl_bit == State::S0)
+ return unchanged_sig;
+ if (ctrl_bit == State::S1)
+ return changed_sig;
+ return module->Mux(NEW_ID, unchanged_sig, changed_sig, ctrl_bit);
+}
+
+void mutate_inv(Design *design, const mutate_opts_t &opts)
+{
+ Module *module = design->module(opts.module);
+ Cell *cell = module->cell(opts.cell);
+
+ SigBit bit = cell->getPort(opts.port)[opts.portbit];
+ SigBit inbit, outbit;
+
+ if (cell->input(opts.port))
+ {
+ log("Add input inverter at %s.%s.%s[%d].\n", log_id(module), log_id(cell), log_id(opts.port), opts.portbit);
+ SigBit outbit = module->Not(NEW_ID, bit);
+ bit = mutate_ctrl_mux(module, opts, bit, outbit);
+ }
+ else
+ {
+ log("Add output inverter at %s.%s.%s[%d].\n", log_id(module), log_id(cell), log_id(opts.port), opts.portbit);
+ SigBit inbit = module->addWire(NEW_ID);
+ SigBit outbit = module->Not(NEW_ID, inbit);
+ module->connect(bit, mutate_ctrl_mux(module, opts, inbit, outbit));
+ bit = inbit;
+ }
+
+ SigSpec s = cell->getPort(opts.port);
+ s[opts.portbit] = bit;
+ cell->setPort(opts.port, s);
+}
+
+void mutate_const(Design *design, const mutate_opts_t &opts, bool one)
+{
+ Module *module = design->module(opts.module);
+ Cell *cell = module->cell(opts.cell);
+
+ SigBit bit = cell->getPort(opts.port)[opts.portbit];
+ SigBit inbit, outbit;
+
+ if (cell->input(opts.port))
+ {
+ log("Add input constant %d at %s.%s.%s[%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit);
+ SigBit outbit = one ? State::S1 : State::S0;
+ bit = mutate_ctrl_mux(module, opts, bit, outbit);
+ }
+ else
+ {
+ log("Add output constant %d at %s.%s.%s[%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit);
+ SigBit inbit = module->addWire(NEW_ID);
+ SigBit outbit = one ? State::S1 : State::S0;
+ module->connect(bit, mutate_ctrl_mux(module, opts, inbit, outbit));
+ bit = inbit;
+ }
+
+ SigSpec s = cell->getPort(opts.port);
+ s[opts.portbit] = bit;
+ cell->setPort(opts.port, s);
+}
+
+void mutate_cnot(Design *design, const mutate_opts_t &opts, bool one)
+{
+ Module *module = design->module(opts.module);
+ Cell *cell = module->cell(opts.cell);
+
+ SigBit bit = cell->getPort(opts.port)[opts.portbit];
+ SigBit ctrl = cell->getPort(opts.port)[opts.ctrlbit];
+ SigBit inbit, outbit;
+
+ if (cell->input(opts.port))
+ {
+ log("Add input cnot%d at %s.%s.%s[%d,%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit, opts.ctrlbit);
+ SigBit outbit = one ? module->Xor(NEW_ID, bit, ctrl) : module->Xnor(NEW_ID, bit, ctrl);
+ bit = mutate_ctrl_mux(module, opts, bit, outbit);
+ }
+ else
+ {
+ log("Add output cnot%d at %s.%s.%s[%d,%d].\n", one ? 1 : 0, log_id(module), log_id(cell), log_id(opts.port), opts.portbit, opts.ctrlbit);
+ SigBit inbit = module->addWire(NEW_ID);
+ SigBit outbit = one ? module->Xor(NEW_ID, inbit, ctrl) : module->Xnor(NEW_ID, inbit, ctrl);
+ module->connect(bit, mutate_ctrl_mux(module, opts, inbit, outbit));
+ bit = inbit;
+ }
+
+ SigSpec s = cell->getPort(opts.port);
+ s[opts.portbit] = bit;
+ cell->setPort(opts.port, s);
+}
+
+struct MutatePass : public Pass {
+ MutatePass() : Pass("mutate", "generate or apply design mutations") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" mutate -list N [options] [selection]\n");
+ log("\n");
+ log("Create a list of N mutations using an even sampling.\n");
+ log("\n");
+ log(" -o filename\n");
+ log(" Write list to this file instead of console output\n");
+ log("\n");
+ log(" -s filename\n");
+ log(" Write a list of all src tags found in the design to the specified file\n");
+ log("\n");
+ log(" -seed N\n");
+ log(" RNG seed for selecting mutations\n");
+ log("\n");
+ log(" -none\n");
+ log(" Include a \"none\" mutation in the output\n");
+ log("\n");
+ log(" -ctrl name width value\n");
+ log(" Add -ctrl options to the output. Use 'value' for first mutation, then\n");
+ log(" simply count up from there.\n");
+ log("\n");
+ log(" -mode name\n");
+ log(" -module name\n");
+ log(" -cell name\n");
+ log(" -port name\n");
+ log(" -portbit int\n");
+ log(" -ctrlbit int\n");
+ log(" -wire name\n");
+ log(" -wirebit int\n");
+ log(" -src string\n");
+ log(" Filter list of mutation candidates to those matching\n");
+ log(" the given parameters.\n");
+ log("\n");
+ log(" -cfg option int\n");
+ log(" Set a configuration option. Options available:\n");
+ log(" weight_pq_w weight_pq_b weight_pq_c weight_pq_s\n");
+ log(" weight_pq_mw weight_pq_mb weight_pq_mc weight_pq_ms\n");
+ log(" weight_cover pick_cover_prcnt\n");
+ log("\n");
+ log("\n");
+ log(" mutate -mode MODE [options]\n");
+ log("\n");
+ log("Apply the given mutation.\n");
+ log("\n");
+ log(" -ctrl name width value\n");
+ log(" Add a control signal with the given name and width. The mutation is\n");
+ log(" activated if the control signal equals the given value.\n");
+ log("\n");
+ log(" -module name\n");
+ log(" -cell name\n");
+ log(" -port name\n");
+ log(" -portbit int\n");
+ log(" -ctrlbit int\n");
+ log(" Mutation parameters, as generated by 'mutate -list N'.\n");
+ log("\n");
+ log(" -wire name\n");
+ log(" -wirebit int\n");
+ log(" -src string\n");
+ log(" Ignored. (They are generated by -list for documentation purposes.)\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ mutate_opts_t opts;
+ string filename;
+ string srcsfile;
+ int N = -1;
+
+ log_header(design, "Executing MUTATE pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-list" && argidx+1 < args.size()) {
+ N = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-o" && argidx+1 < args.size()) {
+ filename = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-s" && argidx+1 < args.size()) {
+ srcsfile = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-seed" && argidx+1 < args.size()) {
+ opts.seed = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-none") {
+ opts.none = true;
+ continue;
+ }
+ if (args[argidx] == "-mode" && argidx+1 < args.size()) {
+ opts.mode = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-ctrl" && argidx+3 < args.size()) {
+ opts.ctrl_name = RTLIL::escape_id(args[++argidx]);
+ opts.ctrl_width = atoi(args[++argidx].c_str());
+ opts.ctrl_value = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-module" && argidx+1 < args.size()) {
+ opts.module = RTLIL::escape_id(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-cell" && argidx+1 < args.size()) {
+ opts.cell = RTLIL::escape_id(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-port" && argidx+1 < args.size()) {
+ opts.port = RTLIL::escape_id(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-portbit" && argidx+1 < args.size()) {
+ opts.portbit = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-ctrlbit" && argidx+1 < args.size()) {
+ opts.ctrlbit = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-wire" && argidx+1 < args.size()) {
+ opts.wire = RTLIL::escape_id(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-wirebit" && argidx+1 < args.size()) {
+ opts.wirebit = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-src" && argidx+1 < args.size()) {
+ opts.src.insert(args[++argidx]);
+ continue;
+ }
+ if (args[argidx] == "-cfg" && argidx+2 < args.size()) {
+ if (args[argidx+1] == "pick_cover_prcnt") {
+ opts.pick_cover_prcnt = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_cover") {
+ opts.weight_cover = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_w") {
+ opts.weight_pq_w = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_b") {
+ opts.weight_pq_b = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_c") {
+ opts.weight_pq_c = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_s") {
+ opts.weight_pq_s = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_mw") {
+ opts.weight_pq_mw = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_mb") {
+ opts.weight_pq_mb = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_mc") {
+ opts.weight_pq_mc = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ if (args[argidx+1] == "weight_pq_ms") {
+ opts.weight_pq_ms = atoi(args[argidx+2].c_str());
+ argidx += 2;
+ continue;
+ }
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (N >= 0) {
+ mutate_list(design, opts, filename, srcsfile, N);
+ return;
+ }
+
+ if (opts.mode == "none") {
+ if (!opts.ctrl_name.empty()) {
+ Module *topmod = opts.module.empty() ? design->top_module() : design->module(opts.module);
+ if (topmod)
+ mutate_ctrl_sig(topmod, opts.ctrl_name, opts.ctrl_width);
+ }
+ return;
+ }
+
+ if (opts.mode == "inv") {
+ mutate_inv(design, opts);
+ return;
+ }
+
+ if (opts.mode == "const0" || opts.mode == "const1") {
+ mutate_const(design, opts, opts.mode == "const1");
+ return;
+ }
+
+ if (opts.mode == "cnot0" || opts.mode == "cnot1") {
+ mutate_cnot(design, opts, opts.mode == "cnot1");
+ return;
+ }
+
+ log_cmd_error("Invalid mode: %s\n", opts.mode.c_str());
+ }
+} MutatePass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/sat/supercover.cc b/passes/sat/supercover.cc
new file mode 100644
index 00000000..ba44f02d
--- /dev/null
+++ b/passes/sat/supercover.cc
@@ -0,0 +1,92 @@
+/*
+ * 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 SupercoverPass : public Pass {
+ SupercoverPass() : Pass("supercover", "add hi/lo cover cells for each wire bit") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" supercover [options] [selection]\n");
+ log("\n");
+ log("This command adds two cover cells for each bit of each selected wire, one\n");
+ log("checking for a hi signal level and one checking for lo level.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ // bool flag_noinit = false;
+
+ log_header(design, "Executing SUPERCOVER pass.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-noinit") {
+ // flag_noinit = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ SigMap sigmap(module);
+ pool<SigBit> handled_bits;
+
+ int cnt_wire = 0, cnt_bits = 0;
+ log("Adding cover cells to module %s.\n", log_id(module));
+ for (auto wire : module->selected_wires())
+ {
+ bool counted_wire = false;
+ std::string src = wire->get_src_attribute();
+
+ for (auto bit : sigmap(SigSpec(wire)))
+ {
+ if (bit.wire == nullptr)
+ continue;
+
+ if (handled_bits.count(bit))
+ continue;
+
+ SigSpec inv = module->Not(NEW_ID, bit);
+ module->addCover(NEW_ID, bit, State::S1, src);
+ module->addCover(NEW_ID, inv, State::S1, src);
+
+ handled_bits.insert(bit);
+ if (!counted_wire) {
+ counted_wire = false;
+ cnt_wire++;
+ }
+ cnt_bits++;
+ }
+ }
+ log(" added cover cells to %d wires, %d bits.\n", cnt_wire, cnt_bits);
+ }
+ }
+} SupercoverPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
index 4faa0ab0..cf9e198a 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -36,6 +36,7 @@ OBJS += passes/techmap/attrmvcp.o
OBJS += passes/techmap/attrmap.o
OBJS += passes/techmap/zinit.o
OBJS += passes/techmap/dff2dffs.o
+OBJS += passes/techmap/flowmap.o
endif
GENFILES += passes/techmap/techmap.inc
diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc
index d2d15a4a..21b70f49 100644
--- a/passes/techmap/abc.cc
+++ b/passes/techmap/abc.cc
@@ -327,8 +327,26 @@ void extract_cell(RTLIL::Cell *cell, bool keepff)
}
}
-std::string remap_name(RTLIL::IdString abc_name)
+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();
+ }
+ }
+ }
std::stringstream sstr;
sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1);
return sstr.str();
@@ -353,12 +371,12 @@ void dump_loop_graph(FILE *f, int &nr, std::map<int, std::set<int>> &edges, std:
}
for (auto n : nodes)
- fprintf(f, " n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit),
+ fprintf(f, " ys__n%d [label=\"%s\\nid=%d, count=%d\"%s];\n", n, log_signal(signal_list[n].bit),
n, in_counts[n], workpool.count(n) ? ", shape=box" : "");
for (auto &e : edges)
for (auto n : e.second)
- fprintf(f, " n%d -> n%d;\n", e.first, n);
+ fprintf(f, " ys__n%d -> ys__n%d;\n", e.first, n);
fprintf(f, "}\n");
}
@@ -624,7 +642,7 @@ struct abc_output_filter
void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file,
std::string liberty_file, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,
bool keepff, std::string delay_target, std::string sop_inputs, std::string sop_products, std::string lutin_shared, bool fast_mode,
- const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode)
+ const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode, bool abc_dress)
{
module = current_module;
map_autoidx = autoidx++;
@@ -728,7 +746,8 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos))
abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3);
-
+ if (abc_dress)
+ abc_script += "; dress";
abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str());
abc_script = add_echos_to_abc_cmd(abc_script);
@@ -784,7 +803,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
for (auto &si : signal_list) {
if (!si.is_port || si.type != G(NONE))
continue;
- fprintf(f, " n%d", si.id);
+ fprintf(f, " ys__n%d", si.id);
pi_map[count_input++] = log_signal(si.bit);
}
if (count_input == 0)
@@ -796,17 +815,17 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
for (auto &si : signal_list) {
if (!si.is_port || si.type == G(NONE))
continue;
- fprintf(f, " n%d", si.id);
+ fprintf(f, " ys__n%d", si.id);
po_map[count_output++] = log_signal(si.bit);
}
fprintf(f, "\n");
for (auto &si : signal_list)
- fprintf(f, "# n%-5d %s\n", si.id, log_signal(si.bit));
+ fprintf(f, "# ys__n%-5d %s\n", si.id, log_signal(si.bit));
for (auto &si : signal_list) {
if (si.bit.wire == NULL) {
- fprintf(f, ".names n%d\n", si.id);
+ fprintf(f, ".names ys__n%d\n", si.id);
if (si.bit == RTLIL::State::S1)
fprintf(f, "1\n");
}
@@ -815,68 +834,68 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
int count_gates = 0;
for (auto &si : signal_list) {
if (si.type == G(BUF)) {
- fprintf(f, ".names n%d n%d\n", si.in1, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d\n", si.in1, si.id);
fprintf(f, "1 1\n");
} else if (si.type == G(NOT)) {
- fprintf(f, ".names n%d n%d\n", si.in1, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d\n", si.in1, si.id);
fprintf(f, "0 1\n");
} else if (si.type == G(AND)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "11 1\n");
} else if (si.type == G(NAND)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "0- 1\n");
fprintf(f, "-0 1\n");
} else if (si.type == G(OR)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "-1 1\n");
fprintf(f, "1- 1\n");
} else if (si.type == G(NOR)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "00 1\n");
} else if (si.type == G(XOR)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "01 1\n");
fprintf(f, "10 1\n");
} else if (si.type == G(XNOR)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "00 1\n");
fprintf(f, "11 1\n");
} else if (si.type == G(ANDNOT)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "10 1\n");
} else if (si.type == G(ORNOT)) {
- fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.id);
fprintf(f, "1- 1\n");
fprintf(f, "-0 1\n");
} else if (si.type == G(MUX)) {
- fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);
fprintf(f, "1-0 1\n");
fprintf(f, "-11 1\n");
} else if (si.type == G(AOI3)) {
- fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);
fprintf(f, "-00 1\n");
fprintf(f, "0-0 1\n");
} else if (si.type == G(OAI3)) {
- fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.id);
fprintf(f, "00- 1\n");
fprintf(f, "--0 1\n");
} else if (si.type == G(AOI4)) {
- fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
fprintf(f, "-0-0 1\n");
fprintf(f, "-00- 1\n");
fprintf(f, "0--0 1\n");
fprintf(f, "0-0- 1\n");
} else if (si.type == G(OAI4)) {
- fprintf(f, ".names n%d n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
+ fprintf(f, ".names ys__n%d ys__n%d ys__n%d ys__n%d ys__n%d\n", si.in1, si.in2, si.in3, si.in4, si.id);
fprintf(f, "00-- 1\n");
fprintf(f, "--00 1\n");
} else if (si.type == G(FF)) {
if (si.init == State::S0 || si.init == State::S1) {
- fprintf(f, ".latch n%d n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0);
+ fprintf(f, ".latch ys__n%d ys__n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0);
recover_init = true;
} else
- fprintf(f, ".latch n%d n%d 2\n", si.in1, si.id);
+ fprintf(f, ".latch ys__n%d ys__n%d 2\n", si.in1, si.id);
} else if (si.type != G(NONE))
log_abort();
if (si.type != G(NONE))
@@ -889,7 +908,6 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n",
count_gates, GetSize(signal_list), count_input, count_output);
log_push();
-
if (count_output > 0)
{
log_header(design, "Executing ABC.\n");
@@ -988,7 +1006,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
log_error("ABC output file does not contain a module `netlist'.\n");
for (auto &it : mapped_mod->wires_) {
RTLIL::Wire *w = it.second;
- RTLIL::Wire *wire = module->addWire(remap_name(w->name));
+ RTLIL::Wire *orig_wire = nullptr;
+ RTLIL::Wire *wire = module->addWire(remap_name(w->name, &orig_wire));
+ if (orig_wire != nullptr && orig_wire->attributes.count("\\src"))
+ wire->attributes["\\src"] = orig_wire->attributes["\\src"];
if (markgroups) wire->attributes["\\abcgroup"] = map_autoidx;
design->select(module, wire);
}
@@ -1213,7 +1234,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
for (auto &si : signal_list)
if (si.is_port) {
char buffer[100];
- snprintf(buffer, 100, "\\n%d", si.id);
+ snprintf(buffer, 100, "\\ys__n%d", si.id);
RTLIL::SigSig conn;
if (si.type != G(NONE)) {
conn.first = si.bit;
@@ -1407,6 +1428,11 @@ struct AbcPass : public Pass {
log(" this attribute is a unique integer for each ABC process started. This\n");
log(" is useful for debugging the partitioning of clock domains.\n");
log("\n");
+ log(" -dress\n");
+ log(" run the 'dress' command after all other ABC commands. This aims to\n");
+ log(" preserve naming by an equivalence check between the original and post-ABC\n");
+ log(" netlists (experimental).\n");
+ log("\n");
log("When neither -liberty nor -lut is used, the Yosys standard cell library is\n");
log("loaded into ABC before the ABC script is executed.\n");
log("\n");
@@ -1441,6 +1467,7 @@ struct AbcPass : public Pass {
std::string delay_target, sop_inputs, sop_products, lutin_shared = "-S 1";
bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true;
bool show_tempdir = false, sop_mode = false;
+ bool abc_dress = false;
vector<int> lut_costs;
markgroups = false;
@@ -1555,6 +1582,10 @@ struct AbcPass : public Pass {
map_mux16 = true;
continue;
}
+ if (arg == "-dress") {
+ abc_dress = true;
+ continue;
+ }
if (arg == "-g" && argidx+1 < args.size()) {
for (auto g : split_tokens(args[++argidx], ",")) {
vector<string> gate_list;
@@ -1704,7 +1735,7 @@ struct AbcPass : public Pass {
if (!dff_mode || !clk_str.empty()) {
abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff,
- delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode);
+ delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode, abc_dress);
continue;
}
@@ -1849,7 +1880,7 @@ struct AbcPass : public Pass {
en_polarity = std::get<2>(it.first);
en_sig = assign_map(std::get<3>(it.first));
abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$",
- keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode);
+ keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode, abc_dress);
assign_map.set(mod);
}
}
diff --git a/passes/techmap/deminout.cc b/passes/techmap/deminout.cc
index 9f0c7bf6..47d0ff41 100644
--- a/passes/techmap/deminout.cc
+++ b/passes/techmap/deminout.cc
@@ -83,9 +83,9 @@ struct DeminoutPass : public Pass {
for (auto bit : sigmap(conn.second))
bits_used.insert(bit);
- if (conn.first == "\\Y" && cell->type.in("$mux", "$pmux", "$_MUX_", "$_TBUF_"))
+ if (conn.first == "\\Y" && cell->type.in("$mux", "$pmux", "$_MUX_", "$_TBUF_", "$tribuf"))
{
- bool tribuf = (cell->type == "$_TBUF_");
+ bool tribuf = (cell->type == "$_TBUF_" || cell->type == "$tribuf");
if (!tribuf) {
for (auto &c : cell->connections()) {
@@ -113,7 +113,8 @@ struct DeminoutPass : public Pass {
{
if (bits_numports[bit] > 1 || bits_inout.count(bit))
new_input = true, new_output = true;
-
+ if (bit == State::S0 || bit == State::S1)
+ new_output = true;
if (bits_written.count(bit)) {
new_output = true;
if (bits_tribuf.count(bit))
diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc
index 3291f5a4..7e104096 100644
--- a/passes/techmap/dff2dffe.cc
+++ b/passes/techmap/dff2dffe.cc
@@ -267,6 +267,10 @@ struct Dff2dffePass : public Pass {
log(" operate in the opposite direction: replace $dffe cells with combinations\n");
log(" of $dff and $mux cells. the options below are ignore in unmap mode.\n");
log("\n");
+ log(" -unmap-mince N\n");
+ log(" Same as -unmap but only unmap $dffe where the clock enable port\n");
+ log(" signal is used by less $dffe than the specified number\n");
+ log("\n");
log(" -direct <internal_gate_type> <external_gate_type>\n");
log(" map directly to external gate type. <internal_gate_type> can\n");
log(" be any internal gate-level FF cell (except $_DFFE_??_). the\n");
@@ -289,6 +293,7 @@ struct Dff2dffePass : public Pass {
log_header(design, "Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n");
bool unmap_mode = false;
+ int min_ce_use = -1;
dict<IdString, IdString> direct_dict;
size_t argidx;
@@ -297,6 +302,11 @@ struct Dff2dffePass : public Pass {
unmap_mode = true;
continue;
}
+ if (args[argidx] == "-unmap-mince" && argidx + 1 < args.size()) {
+ unmap_mode = true;
+ min_ce_use = std::stoi(args[++argidx]);
+ continue;
+ }
if (args[argidx] == "-direct" && argidx + 2 < args.size()) {
string direct_from = RTLIL::escape_id(args[++argidx]);
string direct_to = RTLIL::escape_id(args[++argidx]);
@@ -343,8 +353,21 @@ struct Dff2dffePass : public Pass {
if (!mod->has_processes_warn())
{
if (unmap_mode) {
+ SigMap sigmap(mod);
for (auto cell : mod->selected_cells()) {
if (cell->type == "$dffe") {
+ if (min_ce_use >= 0) {
+ int ce_use = 0;
+ for (auto cell_other : mod->selected_cells()) {
+ if (cell_other->type != cell->type)
+ continue;
+ if (sigmap(cell->getPort("\\EN")) == sigmap(cell_other->getPort("\\EN")))
+ ce_use++;
+ }
+ if (ce_use >= min_ce_use)
+ continue;
+ }
+
RTLIL::SigSpec tmp = mod->addWire(NEW_ID, GetSize(cell->getPort("\\D")));
mod->addDff(NEW_ID, cell->getPort("\\CLK"), tmp, cell->getPort("\\Q"), cell->getParam("\\CLK_POLARITY").as_bool());
if (cell->getParam("\\EN_POLARITY").as_bool())
@@ -355,6 +378,18 @@ struct Dff2dffePass : public Pass {
continue;
}
if (cell->type.substr(0, 7) == "$_DFFE_") {
+ if (min_ce_use >= 0) {
+ int ce_use = 0;
+ for (auto cell_other : mod->selected_cells()) {
+ if (cell_other->type != cell->type)
+ continue;
+ if (sigmap(cell->getPort("\\E")) == sigmap(cell_other->getPort("\\E")))
+ ce_use++;
+ }
+ if (ce_use >= min_ce_use)
+ continue;
+ }
+
bool clk_pol = cell->type.substr(7, 1) == "P";
bool en_pol = cell->type.substr(8, 1) == "P";
RTLIL::SigSpec tmp = mod->addWire(NEW_ID);
diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc
index a8eecc97..48390488 100644
--- a/passes/techmap/dffinit.cc
+++ b/passes/techmap/dffinit.cc
@@ -43,18 +43,37 @@ struct DffinitPass : public Pass {
log(" initial value of 1 or 0. (multi-bit values are not supported in this\n");
log(" mode.)\n");
log("\n");
+ log(" -strinit <string for high> <string for low> \n");
+ log(" use string values in the command line to represent a single-bit\n");
+ log(" initial value of 1 or 0. (multi-bit values are not supported in this\n");
+ log(" mode.)\n");
+ log("\n");
+ log(" -noreinit\n");
+ log(" fail if the FF cell has already a defined initial value set in other\n");
+ log(" passes and the initial value of the net it drives is not equal to\n");
+ log(" the already defined initial value.\n");
+ log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing DFFINIT pass (set INIT param on FF cells).\n");
dict<IdString, dict<IdString, IdString>> ff_types;
- bool highlow_mode = false;
+ bool highlow_mode = false, noreinit = false;
+ std::string high_string, low_string;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-highlow") {
highlow_mode = true;
+ high_string = "high";
+ low_string = "low";
+ continue;
+ }
+ if (args[argidx] == "-strinit" && argidx+2 < args.size()) {
+ highlow_mode = true;
+ high_string = args[++argidx];
+ low_string = args[++argidx];
continue;
}
if (args[argidx] == "-ff" && argidx+3 < args.size()) {
@@ -64,6 +83,10 @@ struct DffinitPass : public Pass {
ff_types[cell_name][output_port] = init_param;
continue;
}
+ if (args[argidx] == "-noreinit") {
+ noreinit = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -112,6 +135,10 @@ struct DffinitPass : public Pass {
continue;
while (GetSize(value.bits) <= i)
value.bits.push_back(State::S0);
+ if (noreinit && value.bits[i] != State::Sx && value.bits[i] != init_bits.at(sig[i]))
+ log_error("Trying to assign a different init value for %s.%s.%s which technically "
+ "have a conflicted init value.\n",
+ log_id(module), log_id(cell), log_id(it.second));
value.bits[i] = init_bits.at(sig[i]);
cleanup_bits.insert(sig[i]);
}
@@ -121,9 +148,9 @@ struct DffinitPass : public Pass {
log_error("Multi-bit init value for %s.%s.%s is incompatible with -highlow mode.\n",
log_id(module), log_id(cell), log_id(it.second));
if (value[0] == State::S1)
- value = Const("high");
+ value = Const(high_string);
else
- value = Const("low");
+ value = Const(low_string);
}
log("Setting %s.%s.%s (port=%s, net=%s) to %s.\n", log_id(module), log_id(cell), log_id(it.second),
diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc
index 416ed2bd..274177a6 100644
--- a/passes/techmap/dfflibmap.cc
+++ b/passes/techmap/dfflibmap.cc
@@ -100,6 +100,18 @@ static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name,
for (auto child : cell->children)
if (child->id == "pin" && child->args.size() == 1 && child->args[0] == pin_name)
return true;
+
+ /* If we end up here, the pin specified in the attribute does not exist, which is an error,
+ or, the attribute contains an expression which we do not yet support.
+ For now, we'll simply produce a warning to let the user know something is up.
+ */
+ if (pin_name.find_first_of("^*|&") == std::string::npos) {
+ log_warning("Malformed liberty file - cannot find pin '%s' in cell '%s' - skipping.\n", pin_name.c_str(), cell->args[0].c_str());
+ }
+ else {
+ log_warning("Found unsupported expression '%s' in pin attribute of cell '%s' - skipping.\n", pin_name.c_str(), cell->args[0].c_str());
+ }
+
return false;
}
@@ -648,8 +660,8 @@ struct DfflibmapPass : public Pass {
map_adff_to_dff("$_DFF_PP0_", "$_DFF_P_");
map_adff_to_dff("$_DFF_PP1_", "$_DFF_P_");
- log(" final dff cell mappings:\n");
- logmap_all();
+ log(" final dff cell mappings:\n");
+ logmap_all();
for (auto &it : design->modules_)
if (design->selected(it.second) && !it.second->get_bool_attribute("\\blackbox"))
diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc
new file mode 100644
index 00000000..0b7931e4
--- /dev/null
+++ b/passes/techmap/flowmap.cc
@@ -0,0 +1,1613 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2018 whitequark <whitequark@whitequark.org>
+ *
+ * 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.
+ *
+ */
+
+// [[CITE]] FlowMap algorithm
+// Jason Cong; Yuzheng Ding, "An Optimal Technology Mapping Algorithm for Delay Optimization in Lookup-Table Based FPGA Designs,"
+// Computer-Aided Design of Integrated Circuits and Systems, IEEE Transactions on, Vol. 13, pp. 1-12, Jan. 1994.
+// doi: 10.1109/43.273754
+
+// [[CITE]] FlowMap-r algorithm
+// Jason Cong; Yuzheng Ding, "On Area/Depth Tradeoff in LUT-Based FPGA Technology Mapping,"
+// Very Large Scale Integration Systems, IEEE Transactions on, Vol. 2, June 1994.
+// doi: 10.1109/92.28574
+
+// Required reading material:
+//
+// Min-cut max-flow theorem:
+// https://www.coursera.org/lecture/algorithms-part2/maxflow-mincut-theorem-beb9G
+// FlowMap paper:
+// http://cadlab.cs.ucla.edu/~cong/papers/iccad92.pdf (short version)
+// https://limsk.ece.gatech.edu/book/papers/flowmap.pdf (long version)
+// FlowMap-r paper:
+// http://cadlab.cs.ucla.edu/~cong/papers/dac93.pdf (short version)
+// https://sci-hub.tw/10.1109/92.285741 (long version)
+
+// Notes on correspondence between paper and implementation:
+//
+// 1. In the FlowMap paper, the nodes are logic elements (analogous to Yosys cells) and edges are wires. However, in our implementation,
+// we use an inverted approach: the nodes are Yosys wire bits, and the edges are derived from (but aren't represented by) Yosys cells.
+// This may seem counterintuitive. Three observations may help understanding this. First, for a cell with a 1-bit Y output that is
+// the sole driver of its output net (which is the typical case), these representations are equivalent, because there is an exact
+// correspondence between cells and output wires. Second, in the paper, primary inputs (analogous to Yosys cell or module ports) are
+// nodes, and in Yosys, inputs are wires; our approach allows a direct mapping from both primary inputs and 1-output logic elements to
+// flow graph nodes. Third, Yosys cells may have multiple outputs or multi-bit outputs, and by using Yosys wire bits as flow graph nodes,
+// such cells are supported without any additional effort; any Yosys cell with n output wire bits ends up being split into n flow graph
+// nodes.
+//
+// 2. The FlowMap paper introduces three networks: Nt, Nt', and Nt''. The network Nt is directly represented by a subgraph of RTLIL graph,
+// which is parsed into an equivalent but easier to traverse representation in FlowmapWorker. The network Nt' is built explicitly
+// from a subgraph of Nt, and uses a similar representation in FlowGraph. The network Nt'' is implicit in FlowGraph, which is possible
+// because of the following observation: each Nt' node corresponds to an Nt'' edge of capacity 1, and each Nt' edge corresponds to
+// an Nt'' edge of capacity ∞. Therefore, we only need to explicitly record flow for Nt' edges and through Nt' nodes.
+//
+// 3. The FlowMap paper ambiguously states: "Moreover, we can find such a cut (X′′, X̅′′) by performing a depth first search starting at
+// the source s, and including in X′′ all the nodes which are reachable from s." This actually refers to a specific kind of search,
+// min-cut computation. Min-cut computation involves computing the set of nodes reachable from s by an undirected path with no full
+// (i.e. zero capacity) forward edges or empty (i.e. no flow) backward edges. In addition, the depth first search is required to compute
+// a max-volume max-flow min-cut specifically, because a max-flow min-cut is not, in general, unique.
+
+// Notes on implementation:
+//
+// 1. To compute depth optimal packing, an intermediate representation is used, where each cell with n output bits is split into n graph
+// nodes. Each such graph node is represented directly with the wire bit (RTLIL::SigBit instance) that corresponds to the output bit
+//